diff --git a/.gitignore b/.gitignore index 245351f..336ad70 100644 --- a/.gitignore +++ b/.gitignore @@ -3,4 +3,4 @@ config.json __init__ __pycache__ - +.vscode/ diff --git a/README.md b/README.md index db05eb2..c0f8e17 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,7 @@ - **Release:** v2.4.1 - **Author:** Corrado Gerbaldo - [IU1BOW](https://www.qrz.com/db/IU1BOW) - **Mail:** -- **Licensing:** Gpl V3.0 see ["LICENSE"](LICENSE) file. +- **Licensing:** Gpl V3.0 see [LICENSE](LICENSE) file. - **Languages:** This application is written in Python 3.11/flask,Javascript and HTML ___ @@ -233,12 +233,69 @@ This application is designed for desktop and mobile phone. It is a [PWA](https:/ ### API **Spot list** + You can retrive last spots calling "**/spotlist**"; For example [www.iu1bow.com/spotlist](https://www.iu1bow.com/spotlist) -**country of a callsign** +**Country of a callsign** You cam retrive some informations about a callsign with **callsign**; For example [www.iu1bow.com/callsign?c=IU1BOW](https://www.iu1bow.com/callsign?c=IU1BOW) + +### Development +**Directory structure** +``` +/ . main application files +├── cfg . configuration files (put here your config.json with your setting) +├── docs . documentation +├── lib . python libs used for the application +├── log . application log +├── scripts . utility scripts for testing, build etc. +├── static . static files css, js, data, html, images etc. +│ ├── css . +│ │ ├── dev . development css not minifyed/uglifyed +│ │ └── rel . release css minifyed/uglifyed (do not change these files) +│ ├── data . application data (world.json) +│ ├── html . +│ │ └── dev . html templates used for build release static html (for offline) +│ │ └── rel . release static html (for offline) +│ ├── images . static images +│ │ └── icons . static icons +│ └── js . +│ ├── dev . development js not minifyed/uglifyed +│ └── rel . release js minifyed/uglifyed (do not change these files) +└── templates . html templates used by python flask for serve the web pages +``` +**Application description** + +The main **server** application ```webapp.py``` is in the root folder. In this application there are routing to html dynamic templates and serves also back-end API. This is whapped by ```wsgi.py``` for using with **bjoern** server. + +Static files (css, js...) are in ```static``` directory: here there are subdirectories: +- ```dev``` where you can edit and modify sources +- ```rel``` here there are release files created with the building process and used in producion + +**Lint** + +For lint javascript I use **ESLint**. You can install with ```npm init @eslint/config``` + pylint ```pip install pylint``` + +**Building process** + +Prerequisites: +| **Component** | **Description** | **Install command** | +|---------------|----------------------------------------------------|---------------------------------| +| npm | a packet manager for javascript | depend on your os. See [official page](https://docs.npmjs.com/downloading-and-installing-node-js-and-npm) | +| uglify-js | npm component used to minify and uglify javascript | ```npm install uglify-js -g``` | +| css-minify | npm component used to minify css | ```npm install css-minify -g``` | +| staticjinja | python module used to create static page starting from a html template | ```pip install staticjinja``` | + + +You can build the software for test (dev), or for production (release) environements. +In ```scripts``` directory launch: +- ```./build.sh -d``` for dev environment + +- ```./build.sh -r``` for release environment + + ### Screenshots ---------- diff --git a/cfg/webapp_log_config.ini b/cfg/webapp_log_config.ini index 119f7ab..2d58b72 100644 --- a/cfg/webapp_log_config.ini +++ b/cfg/webapp_log_config.ini @@ -8,10 +8,11 @@ keys=stream_handler,file_handler keys=formatter [logger_root] -level=INFO +level=INFO handlers=stream_handler,file_handler [handler_stream_handler] +level=INFO class=StreamHandler formatter=formatter args=(sys.stderr,) diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index 3e8ec4b..013d65f 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -1,13 +1,15 @@ ### Change log -Date: 02/01/2023 -Release v.2.4 +Date: 07/01/2023 +Release: v2.4.1 - changed dimensions of spots in world dx spost charts - managed empty data in data providers for charts - removed jQuery: migrated to vanilla javascript - for spot refresh get only new spots starting from last rowid +- modified building script +- moved cty file in data directory ___ Date: 01/01/2023 -Release v.2.4 +Release: v2.4 - migration to python 3.11 - added descriptions to continents - fixed issue #23: Wrong flag for Cocos (Keeling) Islands diff --git a/lib/adxo.py b/lib/adxo.py index 61340c9..d386d74 100644 --- a/lib/adxo.py +++ b/lib/adxo.py @@ -1,78 +1,86 @@ -#*********************************************************************************** +# *********************************************************************************** # Module used to get Announced DX Operation from NG3K website via .ICS (Calendar) # file, parse it and return a dictionary with these events -#*********************************************************************************** -__author__ = 'IU1BOW - Corrado' +# *********************************************************************************** +__author__ = "IU1BOW - Corrado" import requests import logging from datetime import datetime import tempfile -logging.basicConfig(level=logging.INFO,format='%(asctime)s [%(levelname)s]: %(message)s',datefmt='%m/%d/%Y %I:%M:%S') -#format single line +logging.basicConfig( + level=logging.INFO, + format="%(asctime)s [%(levelname)s]: %(message)s", + datefmt="%m/%d/%Y %I:%M:%S", +) +# format single line def format_line(prop): - prop_out=dict() + prop_out = dict() try: - dtstart=datetime.strptime(prop['DTSTART;VALUE=DATE'], '%Y%m%d') - dtend=datetime.strptime(prop['DTEND;VALUE=DATE'], '%Y%m%d') - now=datetime.now() - if dtstart <=now and dtend>=now: - prop_out['start']=dtstart.strftime('%Y-%m-%dT%H:%M:%S%z') - prop_out['end']=dtend.strftime('%Y-%m-%dT%H:%M:%S%z') - prop_out['summary']=prop['SUMMARY'].split('(')[0].strip() - prop_out['callsign']=prop['SUMMARY'].split('(',1)[1].split(')',1)[0] - prop_out['description']=prop['DESCRIPTION'].replace('\\', '') + dtstart = datetime.strptime(prop["DTSTART;VALUE=DATE"], "%Y%m%d") + dtend = datetime.strptime(prop["DTEND;VALUE=DATE"], "%Y%m%d") + now = datetime.now() + if dtstart <= now and dtend >= now: + prop_out["start"] = dtstart.strftime("%Y-%m-%dT%H:%M:%S%z") + prop_out["end"] = dtend.strftime("%Y-%m-%dT%H:%M:%S%z") + prop_out["summary"] = prop["SUMMARY"].split("(")[0].strip() + prop_out["callsign"] = prop["SUMMARY"].split("(", 1)[1].split(")", 1)[0] + prop_out["description"] = prop["DESCRIPTION"].replace("\\", "") except KeyError: pass return prop_out -#TODO: url from conf parameter + +# TODO: url from conf parameter + def get_adxo_events(): - url = 'http://dxcal.kj4z.com/dxcal' - line_num=0 - event_num=0 + url = "http://dxcal.kj4z.com/dxcal" + line_num = 0 + event_num = 0 try: - logging.info('connection to: '+url) - req=requests.get(url) - events=[] - prop=dict() - prop_name='' + logging.info("connection to: " + url) + req = requests.get(url) + events = [] + prop = dict() + prop_name = "" with tempfile.TemporaryFile() as temp: temp.write(req.content) temp.seek(0) - lines=temp.readlines() + lines = temp.readlines() for line_bytes in lines: - line=line_bytes.decode() - line_num+=1 - current_line_array=line.strip().split(':', 1) - if current_line_array[0]=='BEGIN': - if current_line_array[1]=='VCALENDAR': - prop={} - if current_line_array[1]=='VEVENT': - event_num+=1 - prop={} + line = line_bytes.decode() + line_num += 1 + current_line_array = line.strip().split(":", 1) + if current_line_array[0] == "BEGIN": + if current_line_array[1] == "VCALENDAR": + prop = {} + if current_line_array[1] == "VEVENT": + event_num += 1 + prop = {} else: - if current_line_array[0]=='END': - if current_line_array[1]=='VCALENDAR': - pass - if current_line_array[1]=='VEVENT': - prop=format_line(prop) - if prop: - events.append(prop) + if current_line_array[0] == "END": + if current_line_array[1] == "VCALENDAR": + pass + if current_line_array[1] == "VEVENT": + prop = format_line(prop) + if prop: + events.append(prop) else: - if len(current_line_array)>1: - prop_name=current_line_array[0] - prop[prop_name]=current_line_array[1] - else: - if len(prop_name)>0: - prop[prop_name]=prop[prop_name]+current_line_array[0] + if len(current_line_array) > 1: + prop_name = current_line_array[0] + prop[prop_name] = current_line_array[1] + else: + if len(prop_name) > 0: + prop[prop_name] = ( + prop[prop_name] + current_line_array[0] + ) - logging.debug('number of line reads: '+str(line_num)) - logging.info('number ADXO events: '+str(event_num)) + logging.debug("number of line reads: " + str(line_num)) + logging.info("number ADXO events: " + str(event_num)) return events except Exception as e1: logging.error(e1) - return + return diff --git a/lib/config.py b/lib/config.py index bd61a74..10204ad 100644 --- a/lib/config.py +++ b/lib/config.py @@ -1,55 +1,59 @@ -#************************************************************************************* +# ************************************************************************************* # CLI Utility used for manage configuration file -#************************************************************************************* -__author__ = 'IU1BOW - Corrado' +# ************************************************************************************* +__author__ = "IU1BOW - Corrado" import os import os.path from os import path import json -configs = [('mycallsign','Callsign_______________: '), - ('mysql/host','MySql host_____________: '), - ('mysql/db','MySql database_________: '), - ('mysql/user','MySql user_____________: '), - ('mysql/passwd','MySql password_________: '), - ('timer/interval','Spot page refresh(ms)__: ' ), - ('plot_refresh_timer/interval','Plot page refresh(ms)__: '), - ('mail','Mail address___________: '), - ('mail_token','token google 2FA auth__: '), - ('telnet','Telnet address_________: '), - ('enable_cq_filter','Enable cq filter______: ') - ] +configs = [ + ("mycallsign", "Callsign_______________: "), + ("mysql/host", "MySql host_____________: "), + ("mysql/db", "MySql database_________: "), + ("mysql/user", "MySql user_____________: "), + ("mysql/passwd", "MySql password_________: "), + ("timer/interval", "Spot page refresh(ms)__: "), + ("plot_refresh_timer/interval", "Plot page refresh(ms)__: "), + ("mail", "Mail address___________: "), + ("mail_token", "token google 2FA auth__: "), + ("telnet", "Telnet address_________: "), + ("enable_cq_filter", "Enable cq filter______: "), +] + class bcolors: - HEADER = '\033[95m' - OKBLUE = '\033[94m' - OKGREEN = '\033[92m' - WARNING = '\033[93m' - FAIL = '\033[91m' - ENDC = '\033[0m' - BOLD = '\033[1m' - UNDERLINE = '\033[4m' + HEADER = "\033[95m" + OKBLUE = "\033[94m" + OKGREEN = "\033[92m" + WARNING = "\033[93m" + FAIL = "\033[91m" + ENDC = "\033[0m" + BOLD = "\033[1m" + UNDERLINE = "\033[4m" -TEMPLATE_FILE = '../cfg/config.json.template' -USER_FILE = '../cfg/config.json' -#search and open the configuration file + +TEMPLATE_FILE = "../cfg/config.json.template" +USER_FILE = "../cfg/config.json" +# search and open the configuration file def get_cfg_file(template): if template: cfg_file = TEMPLATE_FILE if not path.exists(cfg_file): - print ('file not found') - cfg_file = '' - else: - cfg_file = USER_FILE + print("file not found") + cfg_file = "" + else: + cfg_file = USER_FILE if not path.exists(cfg_file): cfg_file = TEMPLATE_FILE if not path.exists(cfg_file): - cfg_file = '' + cfg_file = "" - print('Configuration file loaded from: '+cfg_file) + print("Configuration file loaded from: " + cfg_file) return cfg_file -#covert file in json + +# covert file in json def get_cfg_json(f): if f: with open(f) as json_data_file: @@ -59,10 +63,11 @@ def get_cfg_json(f): return cfg -#read a single value from json -def get_cfg_value(cfg,key): - k_arr = key.split('/') +# read a single value from json +def get_cfg_value(cfg, key): + + k_arr = key.split("/") l_arr = len(k_arr) try: @@ -71,228 +76,254 @@ def get_cfg_value(cfg,key): elif l_arr == 2: val = cfg[k_arr[0]][k_arr[1]] except KeyError: - val='' + val = "" return val -def set_cfg_value(cfg,key,val): - k_arr = key.split('/') + +def set_cfg_value(cfg, key, val): + k_arr = key.split("/") l_arr = len(k_arr) try: if l_arr == 1: - cfg[k_arr[0]]=val + cfg[k_arr[0]] = val elif l_arr == 2: - cfg[k_arr[0]][k_arr[1]]=val + cfg[k_arr[0]][k_arr[1]] = val except KeyError: pass return cfg -def style_field(lbl,val): - return lbl+bcolors.BOLD + str(val) + bcolors.ENDC -def show_menu(cfg,key): - menu=get_cfg_value(cfg,'menu/menu_list') - i=-1 +def style_field(lbl, val): + return lbl + bcolors.BOLD + str(val) + bcolors.ENDC + + +def show_menu(cfg, key): + menu = get_cfg_value(cfg, "menu/menu_list") + i = -1 for element in menu: - i+=1 - print(style_field(str(i)+'. external: ',str(element['external']))+style_field(', label: ',element['label'])) - print(style_field(' link____: ',element['link'])) - print() + i += 1 + print( + style_field(str(i) + ". external: ", str(element["external"])) + + style_field(", label: ", element["label"]) + ) + print(style_field(" link____: ", element["link"])) + print() return + def help_list(): - print () - print (' h: help') - print (' vc: view config.') - print (' ec: edit config.') - print (' vm: view menu') - print (' em: edit menu') - print (' s: save') - print (' t: load config. from template') - print () - print (' x: exit') - print () + print() + print(" h: help") + print(" vc: view config.") + print(" ec: edit config.") + print(" vm: view menu") + print(" em: edit menu") + print(" s: save") + print(" t: load config. from template") + print() + print(" x: exit") + print() return + def help_menu_edit(): - print () - print (' n: new menu entry') - print (' d: delete menu entry') - print (' e: edit menu entry') - print () - print (' x: exit') - print () + print() + print(" n: new menu entry") + print(" d: delete menu entry") + print(" e: edit menu entry") + print() + print(" x: exit") + print() return -def view(cfg,t): - if t == 'c': - i=0 + +def view(cfg, t): + if t == "c": + i = 0 for element in configs: - (key, lbl)=element - print(style_field(str(i)+'. '+lbl,str(get_cfg_value(cfg,key)))) - i+=1 - elif t == 'm': - print ('Menu:') - show_menu(cfg,'menu/menu_list') + (key, lbl) = element + print(style_field(str(i) + ". " + lbl, str(get_cfg_value(cfg, key)))) + i += 1 + elif t == "m": + print("Menu:") + show_menu(cfg, "menu/menu_list") print() return + def user_input(caption): - return input(caption) + return input(caption) + def edit_config(cfg): - view(cfg,'c') - inp='' - while inp!='x': - inp=str(user_input ('Type the number of config. you would to edit, x for end: ')).lower() + view(cfg, "c") + inp = "" + while inp != "x": + inp = str( + user_input("Type the number of config. you would to edit, x for end: ") + ).lower() if inp.isdigit(): - inp=int(inp) + inp = int(inp) try: - (key, lbl)=configs[inp] - print(style_field(lbl,get_cfg_value(cfg,key))) - val=str(user_input ('Enter new value, [ENTER] for nothing: ')) + (key, lbl) = configs[inp] + print(style_field(lbl, get_cfg_value(cfg, key))) + val = str(user_input("Enter new value, [ENTER] for nothing: ")) except IndexError: - print ('configuration not found!') + print("configuration not found!") finally: - if val!='x' and val !='': - cfg=set_cfg_value(cfg,key,val) + if val != "x" and val != "": + cfg = set_cfg_value(cfg, key, val) return cfg + def menu_delete_entry(cfg): - view(cfg,'m') - inp='' - while inp!='x': - inp=str(user_input ('Choose the menu you would to delete, x for end: ')).lower() + view(cfg, "m") + inp = "" + while inp != "x": + inp = str( + user_input("Choose the menu you would to delete, x for end: ") + ).lower() if inp.isdigit(): - inp=int(inp) - element = cfg['menu']['menu_list'] + inp = int(inp) + element = cfg["menu"]["menu_list"] try: del element[inp] - cfg['menu']['menu_list']=element + cfg["menu"]["menu_list"] = element except IndexError: - print ('menu entry not found!') + print("menu entry not found!") return cfg + def is_external(val): - return val == 'y' + return val == "y" -def menu_input_entry(entry,new_entry): - if not new_entry: - print('label old value: '+entry['label']) - - label=str(user_input('label new value: ')) - if not new_entry: - print('link old value: '+entry['link']) - - link=str(user_input('link value: ')) - if not new_entry: - print('external old value: '+str(entry['external'])) - - external = '' - while external != 'y' and external != 'n': - external=str(user_input('open link external [y/n]: ')).lower() -# if external == 'y': -# external = True -# else: -# external = False - external=is_external(external) +def menu_input_entry(entry, new_entry): + if not new_entry: + print("label old value: " + entry["label"]) - entry['label']=label - entry['link']=link - entry['external']=external + label = str(user_input("label new value: ")) + if not new_entry: + print("link old value: " + entry["link"]) + + link = str(user_input("link value: ")) + if not new_entry: + print("external old value: " + str(entry["external"])) + + external = "" + while external != "y" and external != "n": + external = str(user_input("open link external [y/n]: ")).lower() + + # if external == 'y': + # external = True + # else: + # external = False + external = is_external(external) + + entry["label"] = label + entry["link"] = link + entry["external"] = external return entry + def menu_edit_entry(cfg): - view(cfg,'m') - inp='' - while inp!='x': - inp=str(user_input ('Choose the menu you would to edit, X for end: ')).lower() + view(cfg, "m") + inp = "" + while inp != "x": + inp = str(user_input("Choose the menu you would to edit, X for end: ")).lower() if inp.isdigit(): - inp=int(inp) - element = cfg['menu']['menu_list'] + inp = int(inp) + element = cfg["menu"]["menu_list"] try: - element[inp]=menu_input_entry(element[inp],False) - cfg['menu']['menu_list']=element + element[inp] = menu_input_entry(element[inp], False) + cfg["menu"]["menu_list"] = element except IndexError: - print ('menu entry not found!') + print("menu entry not found!") return cfg + def menu_new_entry(cfg): - view(cfg,'m') - inp='' + view(cfg, "m") + inp = "" valid = False - entry=menu_input_entry({"label":"", "link": "", "external": False},True) + entry = menu_input_entry({"label": "", "link": "", "external": False}, True) while not valid: - inp=str(user_input ('Enter the position number of your menu entry, X for end: ')) + inp = str( + user_input("Enter the position number of your menu entry, X for end: ") + ) if inp.isdigit(): - inp=int(inp) - if inp > len(cfg['menu']['menu_list']): - print('position not valid!') + inp = int(inp) + if inp > len(cfg["menu"]["menu_list"]): + print("position not valid!") valid = False - elif inp == len(cfg['menu']['menu_list']): - cfg['menu']['menu_list'].append(entry) + elif inp == len(cfg["menu"]["menu_list"]): + cfg["menu"]["menu_list"].append(entry) valid = True else: - cfg['menu']['menu_list'].insert(inp,entry) + cfg["menu"]["menu_list"].insert(inp, entry) valid = True else: valid = False return cfg + def edit_menu(cfg): - view(cfg,'m') - inp='' - while inp!='x': + view(cfg, "m") + inp = "" + while inp != "x": help_menu_edit() - inp=str(user_input ('Edit menu> make your choiche: ')).lower() - if (inp == 'n'): - cfg=menu_new_entry(cfg) - elif inp == 'd': - cfg=menu_delete_entry(cfg) - elif inp == 'e': - cfg=menu_edit_entry(cfg) + inp = str(user_input("Edit menu> make your choiche: ")).lower() + if inp == "n": + cfg = menu_new_entry(cfg) + elif inp == "d": + cfg = menu_delete_entry(cfg) + elif inp == "e": + cfg = menu_edit_entry(cfg) return cfg + def save_cfg(cfg): - with open(USER_FILE, 'w') as outfile: - json.dump(cfg, outfile,indent=4) - print ('configuration saved to: '+USER_FILE) + with open(USER_FILE, "w") as outfile: + json.dump(cfg, outfile, indent=4) + print("configuration saved to: " + USER_FILE) return + def main(): print() - print('*** DxSpider configuration ***') - finput=get_cfg_file(False) + print("*** DxSpider configuration ***") + finput = get_cfg_file(False) help_list() - cfg=get_cfg_json(finput) - inp = '' - while inp != 'x' and inp != 'exit': - inp = str(user_input('Main> make your choiche: ')).lower() - if (inp == 'h' or inp =='?' or inp =='help'): + cfg = get_cfg_json(finput) + inp = "" + while inp != "x" and inp != "exit": + inp = str(user_input("Main> make your choiche: ")).lower() + if inp == "h" or inp == "?" or inp == "help": help_list() - elif inp == 'vc': - view(cfg,'c') - elif inp == 'vm': - view(cfg,'m') - elif inp == 'ec': - cfg=edit_config(cfg) - elif inp == 'em': - cfg=edit_menu(cfg) - elif inp == 's' or inp == 'save': + elif inp == "vc": + view(cfg, "c") + elif inp == "vm": + view(cfg, "m") + elif inp == "ec": + cfg = edit_config(cfg) + elif inp == "em": + cfg = edit_menu(cfg) + elif inp == "s" or inp == "save": save_cfg(cfg) - elif inp == 't': - finput=get_cfg_file(True) - cfg=get_cfg_json(finput) + elif inp == "t": + finput = get_cfg_file(True) + cfg = get_cfg_json(finput) -if __name__ == '__main__': + +if __name__ == "__main__": main() diff --git a/lib/cty.py b/lib/cty.py index 628797d..720fa45 100644 --- a/lib/cty.py +++ b/lib/cty.py @@ -1,8 +1,8 @@ -#************************************************************************************* +# ************************************************************************************* # Module used to download cty.dat country file and search callsign in it -#************************************************************************************* -__author__ = 'IU1BOW - Corrado' -import requests +# ************************************************************************************* +__author__ = "IU1BOW - Corrado" +import requests import logging import os import time @@ -10,124 +10,147 @@ from threading import Timer from datetime import datetime import json -logging.basicConfig(level=logging.INFO,format='%(asctime)s [%(levelname)s]: %(message)s',datefmt='%m/%d/%Y %I:%M:%S') -#TODO: url from conf parameter -url = 'https://www.country-files.com/cty/cty_wt_mod.dat' -cty_local=os.path.dirname(__file__)+'/../cfg/cty_wt_mod.dat' -country_file=os.path.dirname(__file__)+'/../cfg/country.json' -#------------------------------------------------------------------------------------- +logging.basicConfig( + level=logging.INFO, + format="%(asctime)s [%(levelname)s]: %(message)s", + datefmt="%m/%d/%Y %I:%M:%S", +) +# TODO: url from conf parameter +url = "https://www.country-files.com/cty/cty_wt_mod.dat" +cty_local = os.path.dirname(__file__) + "/../static/data/cty_wt_mod.dat" +country_file = os.path.dirname(__file__) + "/../cfg/country.json" +# ------------------------------------------------------------------------------------- # download country files cty.dat -#------------------------------------------------------------------------------------- -def download_cty(url,cty_local): +# ------------------------------------------------------------------------------------- +def download_cty(url, cty_local): try: - logging.info('connection to: '+url) - req=requests.get(url) - f=open(cty_local,'wb') + logging.info("connection to: " + url) + req = requests.get(url) + f = open(cty_local, "wb") f.write(req.content) f.close() - logging.info('cty file saved in: '+cty_local) + logging.info("cty file saved in: " + cty_local) return 0 except Exception as e1: logging.error(e1) return 1 -#------------------------------------------------------------------------------------- -# get age of a file in days -#------------------------------------------------------------------------------------- + +# ------------------------------------------------------------------------------------- +# get age of a file in days +# ------------------------------------------------------------------------------------- def file_age_in_days(pathname): - return (time.time() - os.stat(pathname).st_ctime)/(24*3600) + return (time.time() - os.stat(pathname).st_ctime) / (24 * 3600) -#------------------------------------------------------------------------------------- + +# ------------------------------------------------------------------------------------- # manage file cty.dat -#------------------------------------------------------------------------------------- -def get_cty(url,local): +# ------------------------------------------------------------------------------------- +def get_cty(url, local): if os.path.isfile(local): - age=file_age_in_days(local) - if age>7: - logging.info(cty_local+' too old ('+str(round(age,0))+' days): proceding to download it') - return download_cty(url,local) -# else: -# logging.info(cty_local+' updated ('+str(round(age,0))+' days), is not necessary to download it') -# return 0 - logging.info(cty_local+' updated ('+str(round(age,0))+' days), is not necessary to download it') + age = file_age_in_days(local) + if age > 7: + logging.info( + cty_local + + " too old (" + + str(round(age, 0)) + + " days): proceding to download it" + ) + return download_cty(url, local) + # else: + # logging.info(cty_local+' updated ('+str(round(age,0))+' days), is not necessary to download it') + # return 0 + logging.info( + cty_local + + " updated (" + + str(round(age, 0)) + + " days), is not necessary to download it" + ) return 0 - # else: - # logging.info(cty_local+' not present: proceding to download it') - # return download_cty(url,local) + # else: + # logging.info(cty_local+' not present: proceding to download it') + # return download_cty(url,local) - logging.info(cty_local+' not present: proceding to download it') - return download_cty(url,local) + logging.info(cty_local + " not present: proceding to download it") + return download_cty(url, local) -#------------------------------------------------------------------------------------- + +# ------------------------------------------------------------------------------------- # parsing alias and get exceptions -#------------------------------------------------------------------------------------- -def parse_alias(alias,master): +# ------------------------------------------------------------------------------------- +def parse_alias(alias, master): try: - #create a dictionary of array, with start and end position of each exception - find_dict={} - find_dict['pos_cq']=[alias.find('('),alias.find(')')] - find_dict['pos_itu']=[alias.find('['),alias.find(']')] - find_dict['pos_lat_lon']=[alias.find('<'),alias.find('>')] - find_dict['pos_continent']=[alias.find('{'),alias.find('}')] - find_dict['pos_time']=[alias.find('~'),alias[:alias.find('~')].find('~')] + # create a dictionary of array, with start and end position of each exception + find_dict = {} + find_dict["pos_cq"] = [alias.find("("), alias.find(")")] + find_dict["pos_itu"] = [alias.find("["), alias.find("]")] + find_dict["pos_lat_lon"] = [alias.find("<"), alias.find(">")] + find_dict["pos_continent"] = [alias.find("{"), alias.find("}")] + find_dict["pos_time"] = [alias.find("~"), alias[: alias.find("~")].find("~")] - first=9999 - parsed={} + first = 9999 + parsed = {} - #assign default values from master callsing - parsed["country"]=master["country"] - parsed["cq"]=master["cq"] - parsed["itu"]=master["itu"] - parsed["continent"]=master["continent"] - parsed["lat"]=master["lat"] - parsed["lon"]=master["lon"] - parsed["time_loc"]=master["time_loc"] - parsed["full"]=master["full"] - parsed["darc_waedc"]=master["darc_waedc"] + # assign default values from master callsing + parsed["country"] = master["country"] + parsed["cq"] = master["cq"] + parsed["itu"] = master["itu"] + parsed["continent"] = master["continent"] + parsed["lat"] = master["lat"] + parsed["lon"] = master["lon"] + parsed["time_loc"] = master["time_loc"] + parsed["full"] = master["full"] + parsed["darc_waedc"] = master["darc_waedc"] - #extract override cq - if find_dict['pos_cq'][0]>=0: - parsed["cq"]=alias[find_dict['pos_cq'][0]+1:find_dict['pos_cq'][1]] - if find_dict['pos_cq'][0] < first: - first=find_dict['pos_cq'][0] + # extract override cq + if find_dict["pos_cq"][0] >= 0: + parsed["cq"] = alias[find_dict["pos_cq"][0] + 1 : find_dict["pos_cq"][1]] + if find_dict["pos_cq"][0] < first: + first = find_dict["pos_cq"][0] - #extract override itu - if find_dict['pos_itu'][0]>=0: - parsed["itu"]=alias[find_dict['pos_itu'][0]+1:find_dict['pos_itu'][1]] - if find_dict['pos_itu'][0] < first: - first=find_dict['pos_itu'][0] + # extract override itu + if find_dict["pos_itu"][0] >= 0: + parsed["itu"] = alias[find_dict["pos_itu"][0] + 1 : find_dict["pos_itu"][1]] + if find_dict["pos_itu"][0] < first: + first = find_dict["pos_itu"][0] - #extract override lat_lon - if find_dict['pos_lat_lon'][0]>=0: - lat_lon=alias[find_dict['pos_lat_lon'][0]+1:find_dict['pos_lat_lon'][1]] - parsed["lat"]=lat_lon[0:].split('/')[0] - parsed["lon"]=lat_lon[:len(lat_lon)].split('/')[1] - if find_dict['pos_lat_lon'][0] < first: - first=find_dict['pos_lat_lon'][0] + # extract override lat_lon + if find_dict["pos_lat_lon"][0] >= 0: + lat_lon = alias[ + find_dict["pos_lat_lon"][0] + 1 : find_dict["pos_lat_lon"][1] + ] + parsed["lat"] = lat_lon[0:].split("/")[0] + parsed["lon"] = lat_lon[: len(lat_lon)].split("/")[1] + if find_dict["pos_lat_lon"][0] < first: + first = find_dict["pos_lat_lon"][0] - #extract override continent - if find_dict['pos_continent'][0]>=0: - parsed["continent"]=alias[find_dict['pos_continent'][0]+1:find_dict['pos_continent'][1]] - if find_dict['pos_continent'][0] < first: - first=find_dict['pos_continent'][0] + # extract override continent + if find_dict["pos_continent"][0] >= 0: + parsed["continent"] = alias[ + find_dict["pos_continent"][0] + 1 : find_dict["pos_continent"][1] + ] + if find_dict["pos_continent"][0] < first: + first = find_dict["pos_continent"][0] - #extract override time - if find_dict['pos_time'][0]>=0: - parsed["time_loc"]=alias[find_dict['pos_time'][0]+1:find_dict['pos_time'][1]] - if find_dict['pos_time'][0] < first: - first=find_dict['pos_time'][0] + # extract override time + if find_dict["pos_time"][0] >= 0: + parsed["time_loc"] = alias[ + find_dict["pos_time"][0] + 1 : find_dict["pos_time"][1] + ] + if find_dict["pos_time"][0] < first: + first = find_dict["pos_time"][0] - #extract callsign - callsing=alias[:first].upper() - if callsing.startswith('='): - parsed["full"]='y' - callsing=callsing[1:] + # extract callsign + callsing = alias[:first].upper() + if callsing.startswith("="): + parsed["full"] = "y" + callsing = callsing[1:] - if callsing.startswith('*'): - parsed["darc_waedc"]='y' - callsing=callsing[1:] + if callsing.startswith("*"): + parsed["darc_waedc"] = "y" + callsing = callsing[1:] return callsing, parsed @@ -138,43 +161,47 @@ def parse_alias(alias,master): logging.error(alias) return -1 -#------------------------------------------------------------------------------------- + +# ------------------------------------------------------------------------------------- # load file from configuration, containing all world country, with related ISO codes -#------------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------------- def load_country(): - with open(country_file) as json_country: + with open(country_file) as json_country: return json.load(json_country) -#------------------------------------------------------------------------------------- + +# ------------------------------------------------------------------------------------- # search for ISO code, transcoding the country description -#------------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------------- def add_country(table): - country_data=load_country() + country_data = load_country() for key, value in table.items(): - found=0 - for i in country_data['country_codes']: - if i['desc'].upper()==value['country'].upper(): - value["iso"]=i["ISO"] - value["wpx"]=i["WPX"] - found=1 + found = 0 + for i in country_data["country_codes"]: + if i["desc"].upper() == value["country"].upper(): + value["iso"] = i["ISO"] + value["wpx"] = i["WPX"] + found = 1 break - if found==0: - logging.warning('country "'+value['country']+'" not found in cfg/country.json') + if found == 0: + logging.warning( + 'country "' + value["country"] + '" not found in cfg/country.json' + ) return + class prefix_table: - - #------------------------------------------------------------------------------------- + # ------------------------------------------------------------------------------------- # init of the class # - download file cty.dat # - parse file and create prefix_master with all prefixies and attributes - #..................................................................................... + # ..................................................................................... # CTY.DAT Format # # reference: https://www.country-files.com/cty-dat-format/ # - # Note that the fields are aligned in columns and spaced out for readability only. It + # Note that the fields are aligned in columns and spaced out for readability only. It # is the “:” at the end of each field that acts as a delimiter for that field: # Column Length Description # 1 26 Country Name @@ -185,17 +212,17 @@ class prefix_table: # 51 10 Longitude in degrees, + for West # 61 9 Local time offset from GMT # 70 6 Primary DXCC Prefix (A “*” preceding this prefix indicates that the country - # is on the DARC WAEDC list, and counts in CQ-sponsored contests, but not + # is on the DARC WAEDC list, and counts in CQ-sponsored contests, but not # ARRL-sponsored contests). - # - # Alias DXCC prefixes (including the primary one) follow on consecutive lines, - # separated by commas (,). Multiple lines are OK; a line to be continued should end with - # comma (,) though it’s not required. A semi-colon (;) terminates the last alias + # + # Alias DXCC prefixes (including the primary one) follow on consecutive lines, + # separated by commas (,). Multiple lines are OK; a line to be continued should end with + # comma (,) though it’s not required. A semi-colon (;) terminates the last alias # prefix in the list. - # - # If an alias prefix is preceded by ‘=’, this indicates that the prefix is to be treated + # + # If an alias prefix is preceded by ‘=’, this indicates that the prefix is to be treated # as a full callsign, i.e. must be an exact match. - # + # # The following special characters can be applied after an alias prefix: # (#) Override CQ Zone # [#] Override ITU Zone @@ -203,123 +230,127 @@ class prefix_table: # {aa} Override Continent # ~#~ Override local time offset from GMT # - #------------------------------------------------------------------------------------- + # ------------------------------------------------------------------------------------- def __init__(self): global prefix_master - prefix_master=dict() + prefix_master = dict() initialization() return global initialization + def initialization(): refresh() global timer - timer = Timer(3600*24,initialization) #try to refresh once a day + timer = Timer(3600 * 24, initialization) # try to refresh once a day timer.start() return - - #------------------------------------------------------------------------------------- - # refresh data - #------------------------------------------------------------------------------------- + # ------------------------------------------------------------------------------------- + # refresh data + # ------------------------------------------------------------------------------------- global refresh + def refresh(): - - logging.info('CTY: start initialization') - if get_cty(url,cty_local)>0: - logging.error('there is a problem during downloading country files!') - logging.info('continue with previous file') - logging.info('check the connectivity, or put manually the file '+cty_local) - line_num=0 - line_num_valid=0 - entities_number=0 - data='' - table=[] + + logging.info("CTY: start initialization") + if get_cty(url, cty_local) > 0: + logging.error("there is a problem during downloading country files!") + logging.info("continue with previous file") + logging.info( + "check the connectivity, or put manually the file " + cty_local + ) + line_num = 0 + line_num_valid = 0 + entities_number = 0 + data = "" + table = [] prefix_master.clear() try: - with open(cty_local, 'r') as f: - for line in f: - line_num+=1 - li=line.strip() - #remove comments + with open(cty_local, "r") as f: + for line in f: + line_num += 1 + li = line.strip() + # remove comments if not li.startswith("#"): - line_num_valid+=1 - data+=li - logging.info('number of lines reads: '+str(line_num)) - logging.info('number of valid lines: '+str(line_num_valid)) - - #split in array of lines terminated with semicolon - table=data.split(';') - for i,item_table in enumerate(table): - row=item_table.split(':') - #remove trailing spaces and uppercasing - row = [x.strip(' ') for x in row] - if len(row)==9: - #if the row is corret put the row in a master prefix dictionary - entities_number+=1 - single_prefix={} - single_prefix["country"]=row[0] - single_prefix["cq"]=row[1] - single_prefix["itu"]=row[2] - single_prefix["continent"]=row[3] - single_prefix["lat"]=row[4] - single_prefix["lon"]=row[5] - single_prefix["time_loc"]=row[6] - single_prefix["full"]='n' - single_prefix["darc_waedc"]='n' - prefix_master[row[7].upper()]=single_prefix - #managing sub-prefixies - sub_prefixies=row[8].split(',') - for sb in sub_prefixies: - values={} - callsign, values=parse_alias(sb,single_prefix) - prefix_master[callsign]=values + line_num_valid += 1 + data += li + logging.info("number of lines reads: " + str(line_num)) + logging.info("number of valid lines: " + str(line_num_valid)) - logging.info('number of entities: '+str(entities_number)) - logging.info('number of single alias: '+str(len(prefix_master))) + # split in array of lines terminated with semicolon + table = data.split(";") + for i, item_table in enumerate(table): + row = item_table.split(":") + # remove trailing spaces and uppercasing + row = [x.strip(" ") for x in row] + if len(row) == 9: + # if the row is corret put the row in a master prefix dictionary + entities_number += 1 + single_prefix = {} + single_prefix["country"] = row[0] + single_prefix["cq"] = row[1] + single_prefix["itu"] = row[2] + single_prefix["continent"] = row[3] + single_prefix["lat"] = row[4] + single_prefix["lon"] = row[5] + single_prefix["time_loc"] = row[6] + single_prefix["full"] = "n" + single_prefix["darc_waedc"] = "n" + prefix_master[row[7].upper()] = single_prefix + # managing sub-prefixies + sub_prefixies = row[8].split(",") + for sb in sub_prefixies: + values = {} + callsign, values = parse_alias(sb, single_prefix) + prefix_master[callsign] = values + + logging.info("number of entities: " + str(entities_number)) + logging.info("number of single alias: " + str(len(prefix_master))) add_country(prefix_master) - logging.info('memory used for prefix: '+str(prefix_master.__sizeof__())+' bytes') - logging.info('CTY: initialization complete') + logging.info( + "memory used for prefix: " + str(prefix_master.__sizeof__()) + " bytes" + ) + logging.info("CTY: initialization complete") return except Exception as e1: template = "An exception of type {0} occurred. Arguments:\n{1!r}" message = template.format(type(e1).__name__, e1.args) logging.error(message) - return + return - - #------------------------------------------------------------------------------------- + # ------------------------------------------------------------------------------------- # find a callsign - #------------------------------------------------------------------------------------- - def find(self,callsign): + # ------------------------------------------------------------------------------------- + def find(self, callsign): try: - data=dict() - i=len(callsign) - callsign=callsign.strip().upper() - while i>0: + data = dict() + i = len(callsign) + callsign = callsign.strip().upper() + while i > 0: try: - data=prefix_master[callsign[:i]] - data['match']=callsign[:i] + data = prefix_master[callsign[:i]] + data["match"] = callsign[:i] return data except KeyError: pass - i-=1 + i -= 1 except Exception as e1: template = "An exception of type {0} occurred. Arguments:\n{1!r}" message = template.format(type(e1).__name__, e1.args) logging.error(message) - return data + return data - #not found - data["country"]="unknown country" - data["iso"]="xx" + # not found + data["country"] = "unknown country" + data["iso"] = "xx" return data def __del__(self): timer.cancel() - logging.info('prefix_table destroyed') + logging.info("prefix_table destroyed") return diff --git a/lib/dxtelnet.py b/lib/dxtelnet.py index ce07811..76d4019 100644 --- a/lib/dxtelnet.py +++ b/lib/dxtelnet.py @@ -1,74 +1,78 @@ -#************************************************************************************* +# ************************************************************************************* # Module used to interface with telnet cluster and get connected nodes -#************************************************************************************* -__author__ = 'IU1BOW - Corrado' +# ************************************************************************************* +__author__ = "IU1BOW - Corrado" import telnetlib import struct import json import logging -def parse_who(lines): - #print(lines.decode('ascii')) - #create a list o lines and define the structure +def parse_who(lines): + # print(lines.decode('ascii')) + + # create a list o lines and define the structure lines = lines.splitlines() - fmtstring='2x 9s 10s 18s 9s 8s 15s' + fmtstring = "2x 9s 10s 18s 9s 8s 15s" fieldstruct = struct.Struct(fmtstring) - row_headers=('callsign','type','started','name','average_rtt','link') + row_headers = ("callsign", "type", "started", "name", "average_rtt", "link") - #skip first lines and last line - payload=[] - for i in range(3,len(lines)-1): - line=lines[i] - ln=len(line) - - padding=bytes(' ' * (struct.calcsize(fmtstring)-ln),'utf-8') - line=(line+padding) + # skip first lines and last line + payload = [] + for i in range(3, len(lines) - 1): + line = lines[i] + ln = len(line) + + padding = bytes(" " * (struct.calcsize(fmtstring) - ln), "utf-8") + line = line + padding if ln > 10: parse = fieldstruct.unpack_from - fields =list(parse(line)) + fields = list(parse(line)) - for j,item_field in enumerate(fields): + for j, item_field in enumerate(fields): try: - fields[j]=item_field.decode('utf-8').strip() + fields[j] = item_field.decode("utf-8").strip() except AttributeError: print(item_field) - payload.append(dict(zip(row_headers,fields))) + payload.append(dict(zip(row_headers, fields))) - # payload = json.dumps(payload) + # payload = json.dumps(payload) return payload -def who(host,port,user): - WAIT_FOR = b'dxspider >' - TIMEOUT=1 - res=0 +def who(host, port, user): + + WAIT_FOR = b"dxspider >" + TIMEOUT = 1 + res = 0 try: - tn = telnetlib.Telnet(host,port,TIMEOUT) + tn = telnetlib.Telnet(host, port, TIMEOUT) try: - tn.read_until(b"login: ",TIMEOUT) - tn.write(user.encode('ascii') + b"\n") - res=tn.read_until(WAIT_FOR,TIMEOUT) + tn.read_until(b"login: ", TIMEOUT) + tn.write(user.encode("ascii") + b"\n") + res = tn.read_until(WAIT_FOR, TIMEOUT) tn.write(b"who\n") - res=tn.read_until(WAIT_FOR,TIMEOUT) + res = tn.read_until(WAIT_FOR, TIMEOUT) tn.write(b"exit\n") - + except EOFError: - logging.error ('could not autenticate to telnet dxspider host: check user callsign ') - logging.error (res) - res=0 + logging.error( + "could not autenticate to telnet dxspider host: check user callsign " + ) + logging.error(res) + res = 0 except: - logging.error ('could not connect to telnet dxspider host: check host/port') - ret = '' - - if res!=0: - ret=parse_who(res) + logging.error("could not connect to telnet dxspider host: check host/port") + ret = "" + + if res != 0: + ret = parse_who(res) else: - ret='' + ret = "" return ret diff --git a/lib/get_dxcluster_modes.py b/lib/get_dxcluster_modes.py index 9442401..caadc95 100644 --- a/lib/get_dxcluster_modes.py +++ b/lib/get_dxcluster_modes.py @@ -1,9 +1,9 @@ -#************************************************************************************* +# ************************************************************************************* # Module used to convert dxcluster band file to band configuration file modes for # spiderweb # you can use it in build step -#************************************************************************************* -__author__ = 'IU1BOW - Corrado' +# ************************************************************************************* +__author__ = "IU1BOW - Corrado" import logging import os import time @@ -12,47 +12,53 @@ import json import re import sys -logging.basicConfig(level=logging.INFO,format='%(asctime)s [%(levelname)s]: %(message)s',datefmt='%m/%d/%Y %I:%M:%S') -#dxspider_band='/home/sysop/spider/data/bands.pl' -output_modes='../cfg/modes.json' -#------------------------------------------------------------------------------------- +logging.basicConfig( + level=logging.INFO, + format="%(asctime)s [%(levelname)s]: %(message)s", + datefmt="%m/%d/%Y %I:%M:%S", +) +# dxspider_band='/home/sysop/spider/data/bands.pl' +output_modes = "../cfg/modes.json" +# ------------------------------------------------------------------------------------- # reads bands file and convert to json -#------------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------------- def parse(input_file): - line_num=0 - line_num_valid=0 - data='' + line_num = 0 + line_num_valid = 0 + data = "" band_trigger = False try: - with open(input_file, 'r') as f: - for line in f: - line_num+=1 - li=line.strip() - #remove comments + with open(input_file, "r") as f: + for line in f: + line_num += 1 + li = line.strip() + # remove comments if li.startswith("%bands = "): - band_trigger = True + band_trigger = True if li.endswith(");"): - band_trigger = False + band_trigger = False if not li.startswith("#") and band_trigger == True: - line_num_valid+=1 - data+=li + line_num_valid += 1 + data += li logging.debug("first step parsing output: ") logging.debug(data) - #replacing strings in order to obtain a json - data=data.lower() - data=data.replace(" ","") - data=data.replace("bless","") - data=data.replace("%bands=","") - data=data.replace(",'bands'","") - data=re.sub(r"([\"'])(?:(?=(\\?))\2.)*?\1=>", "", data) #remove token like '136khz' => - data=data.replace("=>",":") - data=data.replace("(","") - data=data.replace(")","") - data=re.sub(r'([a-zA-Z]+):', r'"\1":', data) #add quotation around words - data=data.replace(",}","}") - data="["+data+"]" - data=data.replace(",]","]") + # replacing strings in order to obtain a json + data = data.lower() + data = data.replace(" ", "") + data = data.replace("bless", "") + data = data.replace("%bands=", "") + data = data.replace(",'bands'", "") + data = re.sub( + r"([\"'])(?:(?=(\\?))\2.)*?\1=>", "", data + ) # remove token like '136khz' => + data = data.replace("=>", ":") + data = data.replace("(", "") + data = data.replace(")", "") + data = re.sub(r"([a-zA-Z]+):", r'"\1":', data) # add quotation around words + data = data.replace(",}", "}") + data = "[" + data + "]" + data = data.replace(",]", "]") logging.debug("second step parsing output: ") logging.debug(data) @@ -65,18 +71,21 @@ def parse(input_file): logging.error(input_file) return "" -#------------------------------------------------------------------------------------- + +# ------------------------------------------------------------------------------------- # add min max freq element to the related mode in final json -#------------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------------- def add_freq(mode, freq, json_modes): try: - for modes in json_modes['modes']: - if modes["id"]==mode: + for modes in json_modes["modes"]: + if modes["id"] == mode: ind_freq = 0 while ind_freq < len(freq): - modes["freq"].append({"min":freq[ind_freq],"max":freq[ind_freq+1]}) - ind_freq +=2 - + modes["freq"].append( + {"min": freq[ind_freq], "max": freq[ind_freq + 1]} + ) + ind_freq += 2 + return json_modes except Exception as e1: @@ -85,24 +94,27 @@ def add_freq(mode, freq, json_modes): logging.error(message) return {} -#------------------------------------------------------------------------------------- + +# ------------------------------------------------------------------------------------- # reads bands file and convert to json -#------------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------------- def create_output(data): - - json_modes=json.loads('{"modes":[{"id":"cw","freq":[]},{"id":"phone","freq":[]},{"id":"digi","freq":[]}]}') + + json_modes = json.loads( + '{"modes":[{"id":"cw","freq":[]},{"id":"phone","freq":[]},{"id":"digi","freq":[]}]}' + ) try: for element in data: for mode in element: if mode == "band": - pass + pass elif mode == "cw": - json_modes=add_freq("cw",element[mode],json_modes) + json_modes = add_freq("cw", element[mode], json_modes) elif mode == "ssb": - json_modes=add_freq("phone",element[mode],json_modes) + json_modes = add_freq("phone", element[mode], json_modes) else: - json_modes=add_freq("digi",element[mode],json_modes) + json_modes = add_freq("digi", element[mode], json_modes) logging.debug("final step output: ") logging.debug(json.dumps(json_modes)) @@ -115,23 +127,24 @@ def create_output(data): logging.error(input_file) return {} -#************************************************************************************* + +# ************************************************************************************* # Main -#************************************************************************************* -logging.info('RDxSpider band file conversion starting...') -if len(sys.argv)!=2: +# ************************************************************************************* +logging.info("RDxSpider band file conversion starting...") +if len(sys.argv) != 2: logging.error("argument invalid. Specify dxcluster band file") logging.error("use: python get_dxcluster_modes.py input_file") raise Exception() -dxspider_band=sys.argv[1] +dxspider_band = sys.argv[1] parsed = parse(dxspider_band) -if parsed != "" : - json_output=create_output(parsed) - with open(output_modes,'w') as outfile: - json.dump(json_output, outfile,indent=4) - logging.info('modes saved to: '+output_modes) - logging.info('DxSpider band file conversion completed') +if parsed != "": + json_output = create_output(parsed) + with open(output_modes, "w") as outfile: + json.dump(json_output, outfile, indent=4) + logging.info("modes saved to: " + output_modes) + logging.info("DxSpider band file conversion completed") else: logging.error("error on parsing input file") diff --git a/lib/plot_data_provider.py b/lib/plot_data_provider.py index 3d77ff3..0d1a0b0 100644 --- a/lib/plot_data_provider.py +++ b/lib/plot_data_provider.py @@ -1,83 +1,101 @@ -#*********************************************************************************** +# *********************************************************************************** # Module that contain classess for providing data to plotting front-end page -#*********************************************************************************** -__author__ = 'IU1BOW - Corrado' +# *********************************************************************************** +__author__ = "IU1BOW - Corrado" import threading import time from lib.qry import query_manager import pandas as pd import json -#----------------------------------------------------------------------------------- +# ----------------------------------------------------------------------------------- # Base class (as template for other classes) -#----------------------------------------------------------------------------------- +# ----------------------------------------------------------------------------------- class BaseDataProvider: - #glb_data is used for store and return informations to within get_data method - global glb_data - global glb_last_refresh - global glb_response + # glb_data is used for store and return informations to within get_data method + global glb_data + global glb_last_refresh + global glb_response - #refresh is used to refetch data from db and store in memory. you can define a - # time for refresh - def refresh(self): - self.logger.info("Class: %s refresh data",self.__class__.__name__) - return {} - - #return data to the caller - def get_data(self): - self.glb_response = {} - self.glb_response.update({"last_refresh":self.glb_last_refresh}) - return self.glb_response - - #constructor: you have to pass logger, continent list and bands (for frequencies) - # this method call the first refresh - def __init__(self, logger, qm, continents, bands): - self.logger = logger - self.logger.info("Class: %s init start",self.__class__.__name__) - self.qm = qm - self.continents=continents - self.bands=bands - self.refresh() - self.logger.info("Class: %s init end",self.__class__.__name__) - return + # refresh is used to refetch data from db and store in memory. you can define a + # time for refresh + def refresh(self): + self.logger.info("Class: %s refresh data", self.__class__.__name__) + return {} -#----------------------------------------------------------------------------------- + # return data to the caller + def get_data(self): + self.glb_response = {} + self.glb_response.update({"last_refresh": self.glb_last_refresh}) + return self.glb_response + + # constructor: you have to pass logger, continent list and bands (for frequencies) + # this method call the first refresh + def __init__(self, logger, qm, continents, bands): + self.logger = logger + self.logger.info("Class: %s init start", self.__class__.__name__) + self.qm = qm + self.continents = continents + self.bands = bands + self.refresh() + self.logger.info("Class: %s init end", self.__class__.__name__) + return + + +# ----------------------------------------------------------------------------------- # Class for managing data for Continent/Band chart -#----------------------------------------------------------------------------------- +# ----------------------------------------------------------------------------------- class ContinentsBandsProvider(BaseDataProvider): + def __init__(self, logger, qm, continents, bands): + # Calling constructor of base class + super().__init__(logger, qm, continents, bands) - def __init__(self,logger, qm, continents, bands): - # Calling constructor of base class - super().__init__(logger,qm,continents,bands) + def __load_data(self, band_frequencies, continents_cq): - def __load_data(self,band_frequencies, continents_cq): + self.logger.info("Start") + self.logger.info("doing query...") - self.logger.info("Start") - self.logger.info("doing query...") + # construct bands query + bands_qry_string = "CASE " + self.logger.debug(band_frequencies) + for i in range(len(band_frequencies["bands"])): + bands_qry_string += ( + " WHEN freq between " + + str(band_frequencies["bands"][i]["min"]) + + " AND " + + str(band_frequencies["bands"][i]["max"]) + ) + bands_qry_string += ' THEN "' + band_frequencies["bands"][i]["id"] + '"' - #construct bands query - bands_qry_string = 'CASE ' - self.logger.debug(band_frequencies) - for i in range(len(band_frequencies["bands"])): - bands_qry_string+=' WHEN freq between '+str(band_frequencies["bands"][i]["min"])+' AND '+ str(band_frequencies["bands"][i]["max"]) - bands_qry_string+=' THEN "'+band_frequencies["bands"][i]["id"]+'"' + # construct continent region query + spottercq_qry_string = "CASE " + spotcq_qry_string = "CASE " + for i in range(len(continents_cq["continents"])): + spottercq_qry_string += ( + " WHEN spottercq in(" + continents_cq["continents"][i]["cq"] + ")" + ) + spottercq_qry_string += ( + ' THEN "' + continents_cq["continents"][i]["id"] + '"' + ) + spotcq_qry_string += ( + " WHEN spotcq in(" + continents_cq["continents"][i]["cq"] + ")" + ) + spotcq_qry_string += ' THEN "' + continents_cq["continents"][i]["id"] + '"' - #construct continent region query - spottercq_qry_string = 'CASE ' - spotcq_qry_string = 'CASE ' - for i in range(len(continents_cq["continents"])): - spottercq_qry_string+=' WHEN spottercq in('+continents_cq["continents"][i]["cq"]+')' - spottercq_qry_string+=' THEN "' +continents_cq["continents"][i]["id"]+'"' - spotcq_qry_string+=' WHEN spotcq in('+continents_cq["continents"][i]["cq"]+')' - spotcq_qry_string+=' THEN "' +continents_cq["continents"][i]["id"]+'"' - - #construct final query string - qry_string =""" + # construct final query string + qry_string = ( + """ SELECT - """+spottercq_qry_string+""" ELSE spottercq END, - """+spotcq_qry_string+""" ELSE spotcq END, - """+bands_qry_string+""" END, + """ + + spottercq_qry_string + + """ ELSE spottercq END, + """ + + spotcq_qry_string + + """ ELSE spotcq END, + """ + + bands_qry_string + + """ END, count(0) number from spot where @@ -86,91 +104,97 @@ class ContinentsBandsProvider(BaseDataProvider): group by 1, 2, 3 ; """ + ) - self.logger.debug(qry_string) - self.qm.qry(qry_string) - data=self.qm.get_data() - if len(data)==0: - self.logger.warning("no data found") + self.logger.debug(qry_string) + self.qm.qry(qry_string) + data = self.qm.get_data() + if len(data) == 0: + self.logger.warning("no data found") - self.logger.info("query done") - self.logger.debug (data) + self.logger.info("query done") + self.logger.debug(data) - return data + return data - # function for search continent in the global data returned by query and making a cartesian product - # in order to prepare data for heatmap - def __normalize_continent(self,data_list,continent,continents_list, band_list): - data_filtered=[] - for i, item_data in enumerate(data_list): - if item_data[0]==continent and not (item_data[3] is None): - element=[] - element.append(item_data[1]) - element.append(item_data[2]) - element.append(item_data[3]) - data_filtered.append(element) + # function for search continent in the global data returned by query and making a cartesian product + # in order to prepare data for heatmap + def __normalize_continent(self, data_list, continent, continents_list, band_list): + data_filtered = [] + for i, item_data in enumerate(data_list): + if item_data[0] == continent and not (item_data[3] is None): + element = [] + element.append(item_data[1]) + element.append(item_data[2]) + element.append(item_data[3]) + data_filtered.append(element) - cartesian_product = [] + cartesian_product = [] - for j, item_continent in enumerate(continents_list): - for k, item_band in enumerate(band_list): - found=0 - for lis, item_filtered in enumerate(data_filtered): - if item_filtered[0]==item_continent["id"] and item_filtered[1]==item_band["id"]: - #cartesian_product.append(item_filtered) - element=[] - element.append(j) - element.append(k) - element.append(item_filtered[2]) - cartesian_product.append(element) - found=1 - if found==0: - element=[] - element.append(j) - element.append(k) - element.append(0) - cartesian_product.append(element) + for j, item_continent in enumerate(continents_list): + for k, item_band in enumerate(band_list): + found = 0 + for lis, item_filtered in enumerate(data_filtered): + if ( + item_filtered[0] == item_continent["id"] + and item_filtered[1] == item_band["id"] + ): + # cartesian_product.append(item_filtered) + element = [] + element.append(j) + element.append(k) + element.append(item_filtered[2]) + cartesian_product.append(element) + found = 1 + if found == 0: + element = [] + element.append(j) + element.append(k) + element.append(0) + cartesian_product.append(element) - self.logger.debug("cartesian product for continent: "+continent) - self.logger.debug(cartesian_product) - return cartesian_product + self.logger.debug("cartesian product for continent: " + continent) + self.logger.debug(cartesian_product) + return cartesian_product - def refresh(self): - super().refresh() - lcl_data={} - qry_data=self.__load_data(self.bands, self.continents) - for i, item in enumerate(self.continents["continents"]): - continent=item["id"] - data_de=self.__normalize_continent(qry_data,continent,self.continents["continents"],self.bands["bands"]) - lcl_data.update({continent:data_de}) - - self.glb_data=lcl_data - self.glb_last_refresh=time.time() + def refresh(self): + super().refresh() + lcl_data = {} + qry_data = self.__load_data(self.bands, self.continents) + for i, item in enumerate(self.continents["continents"]): + continent = item["id"] + data_de = self.__normalize_continent( + qry_data, continent, self.continents["continents"], self.bands["bands"] + ) + lcl_data.update({continent: data_de}) - threading.Timer(15*60,self.refresh).start() #periodic refresh: set time - return - - def get_data(self,continent_filter): - super().get_data() - self.glb_response.update({"band activity": self.glb_data[continent_filter]}) - return self.glb_response + self.glb_data = lcl_data + self.glb_last_refresh = time.time() -#----------------------------------------------------------------------------------- + threading.Timer(15 * 60, self.refresh).start() # periodic refresh: set time + return + + def get_data(self, continent_filter): + super().get_data() + self.glb_response.update({"band activity": self.glb_data[continent_filter]}) + return self.glb_response + + +# ----------------------------------------------------------------------------------- # Class for managing data for Spots per months chart -#----------------------------------------------------------------------------------- +# ----------------------------------------------------------------------------------- class SpotsPerMounthProvider(BaseDataProvider): + def __init__(self, logger, qm): + # Calling constructor of base class + super().__init__(logger, qm, [], []) - def __init__(self,logger,qm): - # Calling constructor of base class - super().__init__(logger,qm,[],[]) + def __load_data(self): - def __load_data(self): + self.logger.info("Start") + self.logger.info("doing query...") - self.logger.info("Start") - self.logger.info("doing query...") - - #construct final query string - qry_string=""" + # construct final query string + qry_string = """ select month(s1.ym) as referring_month, cast(sum( case @@ -226,55 +250,59 @@ class SpotsPerMounthProvider(BaseDataProvider): group by referring_month ; """ - self.logger.debug(qry_string) - self.qm.qry(qry_string) - data=self.qm.get_data() - if len(data)==0: - self.logger.warning("no data found") + self.logger.debug(qry_string) + self.qm.qry(qry_string) + data = self.qm.get_data() + if len(data) == 0: + self.logger.warning("no data found") - self.logger.info("query done") - self.logger.debug (data) + self.logger.info("query done") + self.logger.debug(data) - return data + return data - def refresh(self): - super().refresh() - lcl_data={} - qry_data=self.__load_data() - - for i, item in enumerate(qry_data): - year_data={'year_0':item[1],'year_1':item[2],'year_2':item[3]} - lcl_data.update({item[0]:year_data}) + def refresh(self): + super().refresh() + lcl_data = {} + qry_data = self.__load_data() - self.logger.debug(lcl_data) - - self.glb_data=lcl_data - self.glb_last_refresh=time.time() + for i, item in enumerate(qry_data): + year_data = {"year_0": item[1], "year_1": item[2], "year_2": item[3]} + lcl_data.update({item[0]: year_data}) - threading.Timer(60*60*24,self.refresh).start() #periodic refresh: set time - return + self.logger.debug(lcl_data) - def get_data(self,): - super().get_data() - self.glb_response.update({"spots_per_month": self.glb_data}) - return self.glb_response + self.glb_data = lcl_data + self.glb_last_refresh = time.time() -#----------------------------------------------------------------------------------- + threading.Timer( + 60 * 60 * 24, self.refresh + ).start() # periodic refresh: set time + return + + def get_data( + self, + ): + super().get_data() + self.glb_response.update({"spots_per_month": self.glb_data}) + return self.glb_response + + +# ----------------------------------------------------------------------------------- # Class for managing data for Spots trend chart -#----------------------------------------------------------------------------------- +# ----------------------------------------------------------------------------------- class SpotsTrend(BaseDataProvider): + def __init__(self, logger, qm): + # Calling constructor of base class + super().__init__(logger, qm, [], []) - def __init__(self,logger,qm): - # Calling constructor of base class - super().__init__(logger,qm,[],[]) + def __load_data(self): - def __load_data(self): + self.logger.info("Start") + self.logger.info("doing query...") - self.logger.info("Start") - self.logger.info("doing query...") - - #construct final query string - qry_string=""" + # construct final query string + qry_string = """ select FROM_UNIXTIME(time,'%Y-%m-%d') as day, count(0) as total @@ -283,82 +311,103 @@ class SpotsTrend(BaseDataProvider): GROUP by 1 ; """ - self.logger.debug(qry_string) - self.qm.qry_pd(qry_string) - df=self.qm.get_data() + self.logger.debug(qry_string) + self.qm.qry_pd(qry_string) + df = self.qm.get_data() - self.logger.info("query done") - self.logger.debug (df) + self.logger.info("query done") + self.logger.debug(df) - if len(df)==0: - self.logger.warning("no data found") + if len(df) == 0: + self.logger.warning("no data found") - #normalize data eliminating peaks - df['day']=pd.to_datetime(df['day']) - df=df.set_index('day') - df=df.resample('D').interpolate(method='pad', limit_direction='forward', axis=0) - df=df.rolling('30D').mean() - df['total']=df['total'].round(0) + # normalize data eliminating peaks + df["day"] = pd.to_datetime(df["day"]) + df = df.set_index("day") + df = df.resample("D").interpolate( + method="pad", limit_direction="forward", axis=0 + ) + df = df.rolling("30D").mean() + df["total"] = df["total"].round(0) - return df + return df - def refresh(self): - super().refresh() - qry_data=self.__load_data() + def refresh(self): + super().refresh() + qry_data = self.__load_data() - lcl_data={} + lcl_data = {} - #iterate panda dataframe - for index, row in qry_data.iterrows(): - lcl_data.update({str(index.date()):row['total']}) + # iterate panda dataframe + for index, row in qry_data.iterrows(): + lcl_data.update({str(index.date()): row["total"]}) - self.logger.debug(lcl_data) - - self.glb_data=lcl_data - self.glb_last_refresh=time.time() + self.logger.debug(lcl_data) - threading.Timer(60*60*24,self.refresh).start() #periodic refresh: set time - return + self.glb_data = lcl_data + self.glb_last_refresh = time.time() - def get_data(self): - super().get_data() - self.glb_response.update({"spots_trend": self.glb_data}) - return self.glb_response + threading.Timer( + 60 * 60 * 24, self.refresh + ).start() # periodic refresh: set time + return -#----------------------------------------------------------------------------------- + def get_data(self): + super().get_data() + self.glb_response.update({"spots_trend": self.glb_data}) + return self.glb_response + + +# ----------------------------------------------------------------------------------- # Class for managing data for Hour/Band chart -#----------------------------------------------------------------------------------- +# ----------------------------------------------------------------------------------- class HourBand(BaseDataProvider): + def __init__(self, logger, qm, bands): + # Calling constructor of base class + super().__init__(logger, qm, [], bands) - def __init__(self,logger,qm,bands): - # Calling constructor of base class - super().__init__(logger,qm,[],bands) + def __load_data(self): - def __load_data(self): + self.logger.info("Start") + self.logger.info("doing query...") - self.logger.info("Start") - self.logger.info("doing query...") + self.logger.debug(self.bands) + # construct bands query + bands_qry_string = "CASE " + for i in range(len(self.bands["bands"])): + bands_qry_string += ( + " WHEN freq between " + + str(self.bands["bands"][i]["min"]) + + " AND " + + str(self.bands["bands"][i]["max"]) + ) + bands_qry_string += ' THEN "' + self.bands["bands"][i]["id"] + '"' - self.logger.debug(self.bands) - #construct bands query - bands_qry_string = 'CASE ' - for i in range(len(self.bands["bands"])): - bands_qry_string+=' WHEN freq between '+str(self.bands["bands"][i]["min"])+' AND '+ str(self.bands["bands"][i]["max"]) - bands_qry_string+=' THEN "'+self.bands["bands"][i]["id"]+'"' + # construct bands query weight + bands_weight_qry_string = "CASE " + for i in range(len(self.bands["bands"])): + bands_weight_qry_string += ( + " WHEN freq between " + + str(self.bands["bands"][i]["min"]) + + " AND " + + str(self.bands["bands"][i]["max"]) + ) + bands_weight_qry_string += ( + ' THEN "' + str(self.bands["bands"][i]["min"]) + '"' + ) - #construct bands query weight - bands_weight_qry_string = 'CASE ' - for i in range(len(self.bands["bands"])): - bands_weight_qry_string+=' WHEN freq between '+str(self.bands["bands"][i]["min"])+' AND '+ str(self.bands["bands"][i]["max"]) - bands_weight_qry_string+=' THEN "'+str(self.bands["bands"][i]["min"])+'"' - - #construct final query string - qry_string =""" + # construct final query string + qry_string = ( + """ select s1.band, s1.hour, s1.total from ( SELECT cast(concat(HOUR (FROM_UNIXTIME(time))) as unsigned) as hour, - """+bands_qry_string+""" ELSE "other" END as band, - cast("""+bands_weight_qry_string+""" ELSE 0 END as unsigned) as band_weight, + """ + + bands_qry_string + + """ ELSE "other" END as band, + cast(""" + + bands_weight_qry_string + + """ ELSE 0 END as unsigned) as band_weight, count(0) AS total from spot WHERE FROM_UNIXTIME(time) > DATE_SUB(now(), INTERVAL 1 MONTH) @@ -368,60 +417,63 @@ class HourBand(BaseDataProvider): order by s1.band, s1.hour ; """ + ) - self.logger.debug(qry_string) - self.qm.qry(qry_string) - data=self.qm.get_data() - if len(data)==0: - self.logger.warning("no data found") + self.logger.debug(qry_string) + self.qm.qry(qry_string) + data = self.qm.get_data() + if len(data) == 0: + self.logger.warning("no data found") - self.logger.info("query done") - self.logger.debug (data) + self.logger.info("query done") + self.logger.debug(data) - return data + return data - def refresh(self): - super().refresh() - lcl_data={} - qry_data=self.__load_data() + def refresh(self): + super().refresh() + lcl_data = {} + qry_data = self.__load_data() - for i,j,k in qry_data: - if i not in lcl_data: - lcl_data[i]={} - lcl_data[i].update({j:k}) + for i, j, k in qry_data: + if i not in lcl_data: + lcl_data[i] = {} + lcl_data[i].update({j: k}) - self.logger.debug(lcl_data) - - self.glb_data=lcl_data - self.glb_last_refresh=time.time() + self.logger.debug(lcl_data) - threading.Timer(60*60*24,self.refresh).start() #periodic refresh: set time - return + self.glb_data = lcl_data + self.glb_last_refresh = time.time() - def get_data(self): - super().get_data() - self.glb_response.update({"hour_band": self.glb_data}) - return self.glb_response + threading.Timer( + 60 * 60 * 24, self.refresh + ).start() # periodic refresh: set time + return + + def get_data(self): + super().get_data() + self.glb_response.update({"hour_band": self.glb_data}) + return self.glb_response -#----------------------------------------------------------------------------------- +# ----------------------------------------------------------------------------------- # Class for managing data for World DX SPOTS current activity -#----------------------------------------------------------------------------------- +# ----------------------------------------------------------------------------------- class WorldDxSpotsLive(BaseDataProvider): - global glb_pfxt + global glb_pfxt - def __init__(self,logger,qm,pfxt): - # Calling constructor of base class - self.glb_pfxt=pfxt - super().__init__(logger,qm,[],[]) + def __init__(self, logger, qm, pfxt): + # Calling constructor of base class + self.glb_pfxt = pfxt + super().__init__(logger, qm, [], []) - def __load_data(self): - self.logger.info("Start") - self.logger.info("doing query...") + def __load_data(self): + self.logger.info("Start") + self.logger.info("doing query...") - #construct final query string - qry_string =""" + # construct final query string + qry_string = """ select spotcall as dx from spot WHERE FROM_UNIXTIME(time) > DATE_SUB(now(), INTERVAL 1 HOUR) @@ -429,70 +481,70 @@ class WorldDxSpotsLive(BaseDataProvider): group by 1; """ - self.logger.debug(qry_string) - self.qm.qry(qry_string) - data=self.qm.get_data() - row_headers=self.qm.get_headers() - if len(data)==0: - self.logger.warning("no data found") + self.logger.debug(qry_string) + self.qm.qry(qry_string) + data = self.qm.get_data() + row_headers = self.qm.get_headers() + if len(data) == 0: + self.logger.warning("no data found") - self.logger.info("query done") - self.logger.debug (data) + self.logger.info("query done") + self.logger.debug(data) - #define country table for search info on callsigns - df = pd.DataFrame(columns=['row_id','dx','lat','lon']) - dx=[] - lat=[] - lon=[] - row_id=[] - idx=0 + # define country table for search info on callsigns + df = pd.DataFrame(columns=["row_id", "dx", "lat", "lon"]) + dx = [] + lat = [] + lon = [] + row_id = [] + idx = 0 - for result in data: - main_result=dict(zip(row_headers,result)) - # find the country in prefix table - search_prefix=self.glb_pfxt.find(main_result["dx"]) - if search_prefix["country"] != "unknown country" : - # merge recordset and contry prefix - dx.append(main_result["dx"]) - lon.append(float(search_prefix["lat"])) - lat.append(-float(search_prefix["lon"])) - idx+=1 - row_id.append(idx) + for result in data: + main_result = dict(zip(row_headers, result)) + # find the country in prefix table + search_prefix = self.glb_pfxt.find(main_result["dx"]) + if search_prefix["country"] != "unknown country": + # merge recordset and contry prefix + dx.append(main_result["dx"]) + lon.append(float(search_prefix["lat"])) + lat.append(-float(search_prefix["lon"])) + idx += 1 + row_id.append(idx) - df['dx']=dx - df['lat']=lat - df['lon']=lon - df['row_id']=row_id - df_grp=df.groupby(["lat", "lon"])["row_id"].count().reset_index(name="count") - - if df is None ==0: - logger.warning("no data found") + df["dx"] = dx + df["lat"] = lat + df["lon"] = lon + df["row_id"] = row_id + df_grp = df.groupby(["lat", "lon"])["row_id"].count().reset_index(name="count") - return df_grp + if df is None == 0: + logger.warning("no data found") - def refresh(self): - super().refresh() - lcl_data={} - qry_data=self.__load_data() + return df_grp - self.logger.debug(qry_data) + def refresh(self): + super().refresh() + lcl_data = {} + qry_data = self.__load_data() - lcl_data=[] - for index, row in qry_data.iterrows(): - record=dict(lat=row['lat'], lon=row['lon'], count=row['count']) - lcl_data.append(record) + self.logger.debug(qry_data) - self.logger.debug(lcl_data) - - self.glb_data=lcl_data - self.glb_last_refresh=time.time() + lcl_data = [] + for index, row in qry_data.iterrows(): + record = dict(lat=row["lat"], lon=row["lon"], count=row["count"]) + lcl_data.append(record) - threading.Timer(5*60,self.refresh).start() #periodic refresh: set time - return + self.logger.debug(lcl_data) - def get_data(self): - super().get_data() - - self.glb_response.update({"world_dx_spots_live": self.glb_data}) - - return self.glb_response + self.glb_data = lcl_data + self.glb_last_refresh = time.time() + + threading.Timer(5 * 60, self.refresh).start() # periodic refresh: set time + return + + def get_data(self): + super().get_data() + + self.glb_response.update({"world_dx_spots_live": self.glb_data}) + + return self.glb_response diff --git a/lib/qry.py b/lib/qry.py index a4b0b25..6dfb130 100644 --- a/lib/qry.py +++ b/lib/qry.py @@ -1,55 +1,63 @@ -#***************************************************************************************** -# module used to make query to mysql +# ***************************************************************************************** +# module used to make query to mysql # TODO: manage polymorfism and use only one qry sign -#***************************************************************************************** -#import MySQLdb as my +# ***************************************************************************************** +# import MySQLdb as my import mysql.connector as my from mysql.connector import pooling -import logging +import logging import json import pandas as pd -logging.basicConfig(level=logging.INFO,format='%(asctime)s [%(levelname)s]: %(message)s',datefmt='%m/%d/%Y %I:%M:%S') +logging.basicConfig( + level=logging.INFO, + format="%(asctime)s [%(levelname)s]: %(message)s", + datefmt="%m/%d/%Y %I:%M:%S", +) + class query_manager: -#connection definition + # connection definition - def __init__(self): + def __init__(self): try: - with open('../cfg/config.json') as json_data_file: - cfg = json.load(json_data_file) + with open("../cfg/config.json") as json_data_file: + cfg = json.load(json_data_file) except Exception as e1: logging.info(e1) - logging.info('trying with other path...') + logging.info("trying with other path...") try: - with open ('cfg/config.json') as json_data_file: + with open("cfg/config.json") as json_data_file: cfg = json.load(json_data_file) except Exception as e2: logging.error(e2) - return + return - logging.info('config file loaded') - self.__cnxpool = pooling.MySQLConnectionPool(host=cfg['mysql']['host'], - user=cfg['mysql']['user'], - passwd=cfg['mysql']['passwd'], - db=cfg['mysql']['db'], - charset='latin1', -# charset='utf8mb4', -# collation = 'utf8mb4_general_ci', - pool_name = "spider_pool", - use_pure = True, - pool_size = 3 - ) - logging.info('db connection pool created') + logging.info("config file loaded") + self.__cnxpool = pooling.MySQLConnectionPool( + host=cfg["mysql"]["host"], + user=cfg["mysql"]["user"], + passwd=cfg["mysql"]["passwd"], + db=cfg["mysql"]["db"], + charset="latin1", + # charset='utf8mb4', + # collation = 'utf8mb4_general_ci', + pool_name="spider_pool", + use_pure=True, + pool_size=3, + ) + logging.info("db connection pool created") - #normal query - def qry(self,qs,prepared_statement=False): + # normal query + def qry(self, qs, prepared_statement=False): try: - cnx=self.__cnxpool.get_connection() + cnx = self.__cnxpool.get_connection() cursor = cnx.cursor(prepared=prepared_statement) cursor.execute(qs) - self.__data=cursor.fetchall() - self.__row_headers=[x[0] for x in cursor.description] #this will extract row headers + self.__data = cursor.fetchall() + self.__row_headers = [ + x[0] for x in cursor.description + ] # this will extract row headers cursor.close() except Exception as e2: logging.error(e2) @@ -62,11 +70,11 @@ class query_manager: def get_headers(self): return self.__row_headers - #query with pandas - def qry_pd(self,qs): + # query with pandas + def qry_pd(self, qs): try: - cnx=self.__cnxpool.get_connection() - self.__data = pd.read_sql(qs,con=cnx) + cnx = self.__cnxpool.get_connection() + self.__data = pd.read_sql(qs, con=cnx) except Exception as e2: logging.error(e2) finally: diff --git a/lib/static_build.py b/lib/static_build.py index a92ef3e..6693d32 100644 --- a/lib/static_build.py +++ b/lib/static_build.py @@ -1,15 +1,20 @@ # # little script used to build static pages # -__author__ = 'IU1BOW - Corrado' +__author__ = "IU1BOW - Corrado" from staticjinja import Site + def cookies_check(): - return False + return False if __name__ == "__main__": - site = Site.make_site(searchpath="../static/html/templates/",outpath="../static/html/",env_globals={ - 'cookies_check':cookies_check, - }) - site.render(use_reloader=False) + site = Site.make_site( + searchpath="../static/html/dev/", + outpath="../static/html/rel/", + env_globals={ + "cookies_check": cookies_check, + }, + ) + site.render(use_reloader=False) diff --git a/nohup_test.sh b/nohup_test.sh index 7a91a1f..86b32a0 100755 --- a/nohup_test.sh +++ b/nohup_test.sh @@ -1 +1 @@ -nohup ./test.sh >/dev/null 2>&1 & +nohup ./test.sh -r >/dev/null 2>&1 & diff --git a/requirements.txt b/requirements.txt index 2774beb..4cd9eee 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,5 +1,7 @@ +astroid==2.12.14 charset-normalizer==2.1.1 click==8.1.3 +dill==0.3.6 docopt-ng==0.8.1 easywatch==0.0.5 Flask==2.2.2 @@ -7,14 +9,18 @@ Flask-Minify==0.41 Flask-WTF==1.0.1 htmlmin==0.1.12 idna==3.4 +isort==5.11.4 itsdangerous==2.1.2 Jinja2==3.1.2 jsmin==3.0.1 +lazy-object-proxy==1.9.0 lesscpy==0.15.1 MarkupSafe==2.1.1 +mccabe==0.7.0 mysql-connector-python==8.0.31 numpy==1.24.1 pandas==1.5.2 +platformdirs==2.6.2 ply==3.11 protobuf==4.21.12 python-dateutil==2.8.2 @@ -22,8 +28,10 @@ pytz==2022.7 rcssmin==1.1.1 requests==2.28.1 six==1.16.0 +tomlkit==0.11.6 urllib3==1.26.13 watchdog==2.2.0 Werkzeug==2.2.2 +wrapt==1.14.1 WTForms==3.0.1 xxhash==3.1.0 diff --git a/scripts/build.sh b/scripts/build.sh index 9847a15..728ae86 100755 --- a/scripts/build.sh +++ b/scripts/build.sh @@ -8,13 +8,141 @@ path_static='../static' path_static_html=${path_static}'/html' path_static_js=${path_static}'/js' path_static_css=${path_static}'/css' +path_cfg='../cfg' +app_ini=${path_cfg}'/webapp_log_config.ini' path_docs='../docs' readme='../README.md' manifest=${path_static}'/manifest.webmanifest' changelog=${path_docs}'/'CHANGELOG.md -#echo '*** SPIDERWEB building process ***' +html_change_references(){ + for i in ${path_templates}/*.html + do + echo "changing references to scripts in ${i}" + if [ "${1}" == "-r" ]; then + # concatenating 2 sed command with ";" + # 1) if found static/js/dev/ replace .js with .min.js + # 2) replace static/js/dev/ with static/js/rel/ + if ! sed -i '/static\/js\/dev/s/\.js/\.min\.js/;s/static\/js\/dev/static\/js\/rel/g' ${i}; then + echo 'ERROR replacing .js to .min.js ' + exit 6 + fi + # the same but for css + if ! sed -i '/static\/css\/dev/s/\.css/\.min\.css/;s/static\/css\/dev/static\/css\/rel/g' ${i}; then + echo 'ERROR replacing .css to .min.css ' + exit 6 + fi + elif [ "${1}" == "-d" ]; then + # concatenating 2 sed command with ";" + # 1) if found static/js/rel/ replace .min.js with .js + # 2) replace static/js/rel/ with static/js/dev/ + if ! sed -i '/static\/js\/rel/s/\.min\.js/\.js/;s/static\/js\/rel/static\/js\/dev/g' ${i}; then + echo 'ERROR replacing .min.js to .js' + exit 6 + fi + # the same but for css + if ! sed -i '/static\/css\/rel/s/\.min\.css/\.css/;s/static\/css\/rel/static\/css\/dev/g' ${i}; then + echo 'ERROR replacing .min.css to .css' + exit 6 + fi + fi + done +} + + +if [ "$1" != "-r" ] && [ "$1" != "-d" ]; then + echo 'wrong options:' + echo ' -d: debug' + echo ' -r: release' + exit 5 +fi + +echo '*** SPIDERWEB building process ***' +if [ "$1" == "-r" ]; then + echo 'creating RELEASE application' + + #used to minify the application javascript + echo 'minify javascripts...' + shopt -s extglob + rm ${path_static_js}/rel/*.js + for i in ${path_static_js}/dev/*.js + do + [[ -e ${i} ]] || break # handle the case of no files found + file_no_ext=$(basename "${i%.js}") + out=${path_static_js}/rel/${file_no_ext}.min.js + echo "${i} --> ${out}" + if ! uglifyjs --compress --mangle -- ${i} > ${out} + then + echo 'ERROR minifying javascript: '${i} + shopt -u extglob + exit 80 + fi + if [ ! -s "${out}" ]; then + echo "File is empty" + shopt -u extglob + exit 81 + fi + done + + #used to minify css + echo 'minify css...' + rm ${path_static_css}/rel/*.css + for i in ${path_static_css}/dev/*.css + do + [[ -e ${i} ]] || break # handle the case of no files found + echo ${i} + file_no_ext=$(basename "${i%.css}") + out=${path_static_css}/rel/${file_no_ext}.min.css + echo "${i} --> ${out}" + #if ! curl -X POST -s --fail --compressed --data "code_type=css" --data-urlencode 'code@'${i} https://htmlcompressor.com/compress > ${path_static_css}/rel/${file_no_ext}.min.css + if ! css-minify -f ${i} -o ${path_static_css}/rel/ + then + echo 'ERROR minifying css: ' ${out} + shopt -u extglob + exit 90 + fi + if [ ! -s "${out}" ]; then + echo "File is empty" + shopt -u extglob + exit 91 + fi + #sleep 5 + done + shopt -u extglob + html_change_references -r + + echo 'writing requirements...' + if ! pip freeze|tee ../requirements.txt + then + echo 'ERROR wrinting requirements' + exit 60 + fi + + echo 'remove some packages from requirements...' + sed -i '/certifi==/d' ../requirements.txt + sed -i '/staticjinja==/d' ../requirements.txt + + if ! sed -i 's/level=DEBUG/level=INFO/g' ${app_ini}; then + echo 'ERROR settimg loglevel=INFO ' + exit 12 + fi + +fi + +if [ "$1" == "-d" ]; then + echo 'creating DEBUG application' + html_change_references -d + + if ! sed -i 's/level=INFO/level=DEBUG/g' ${app_ini}; then + echo 'ERROR settimg loglevel=DEBUG ' + exit 12 + fi + +fi + + #echo 'create modes.json from dxspider bands.pl' + #python ../lib/get_dxcluster_modes.py /home/sysop/spider/data/bands.pl #if [ "$?" != "0" ]; then # echo 'ERROR on creating modes.json from dxspider bands.pl' @@ -48,14 +176,14 @@ then fi echo 'writing version in '${changelog} '...' -if ! sed -i '1,4s/Release: v.*/Release: '$ver'/g' ${changelog} +if ! sed -i '1,4s/Release: v.*/Release: '$ver'/g' ${changelog} then echo 'ERROR writing version in '${changelog} exit 35 fi echo 'writing date in '${changelog} '...' -#sed -i '1,4s/Date: [0-9][0-9]\/[0-9][0-9]\/[0-9][0-9][0-9][0-9]/Date: '`date '+%d\/%m\/%Y'`'/g' ${changelog} + if ! sed -i '1,4s/Date: [0-9][0-9]\/[0-9][0-9]\/[0-9][0-9][0-9][0-9]/Date: '$(date '+%d\/%m\/%Y')'/g' ${changelog} then echo 'ERROR writing date in '${changelog} @@ -76,58 +204,5 @@ then exit 50 fi -echo 'writing requirements...' -if ! pip freeze|tee ../requirements.txt -then - echo 'ERROR wrinting requirements' - exit 60 -fi - -echo 'remove some packages...' -sed -i '/certifi==/d' ../requirements.txt -sed -i '/staticjinja==/d' ../requirements.txt - -#used to minify the application javascript -echo 'minify javascripts...' -shopt -s extglob -for i in ${path_static_js}/!(*[m][i][n].js|*.md) -do - [[ -e ${i} ]] || break # handle the case of no files found - echo ${i} - file_no_ext=$(basename "${i%.js}") - if ! curl -X POST -s --fail --compressed --data "code_type=js" --data "js_engine=closure" --data-urlencode 'code@'${i} https://htmlcompressor.com/compress > ${path_static_js}/${file_no_ext}.min.js - then - echo 'ERROR minifying javascript: '${i} - shopt -u extglob - exit 80 - fi - if [ ! -s "${path_static_js}/${file_no_ext}.min.js" ]; then - echo "File is empty" - shopt -u extglob - exit 81 - fi - sleep 3 -done -#used to minify css -echo 'minify css...' -for i in ${path_static_css}/!(*[m][i][n].css|*.md) -do - [[ -e ${i} ]] || break # handle the case of no files found - echo ${i} - file_no_ext=$(basename "${i%.css}") - if ! curl -X POST -s --fail --compressed --data "code_type=css" --data-urlencode 'code@'${i} https://htmlcompressor.com/compress > ${path_static_css}/${file_no_ext}.min.css - then - echo 'ERROR minifying css: '${i} - shopt -u extglob - exit 90 - fi - if [ ! -s "${path_static_css}/${file_no_ext}.min.css" ]; then - echo "File is empty" - shopt -u extglob - exit 91 - fi - sleep 3 -done -shopt -u extglob echo echo Build ok diff --git a/static/css/dev/README.md b/static/css/dev/README.md new file mode 100644 index 0000000..01471a3 --- /dev/null +++ b/static/css/dev/README.md @@ -0,0 +1,2 @@ +This folder `static/css/dev/` contains the style scripts for **development** environment. +You can change these scripts as you want and rebuild the application \ No newline at end of file diff --git a/static/css/dev/style.css b/static/css/dev/style.css new file mode 100644 index 0000000..60fe166 --- /dev/null +++ b/static/css/dev/style.css @@ -0,0 +1,138 @@ +@import url("https://cdn.jsdelivr.net/npm/bootstrap-icons@1.10.1/font/bootstrap-icons.css"); + +@font-face { + font-display: swap; + font-family: 'bootstrap-icons'; + +} + +.badge-responsive { + width: 70px; +} + +@media screen and (max-width: 768px) { + .text-responsive { + font-size: 11px; + } + + .badge-responsive { + width: 40px; + } +} + +@media screen and (max-width: 768px) { + #collapseFilters.collapsing { + position: absolute !important; + z-index: 20; + } + + #collapseFilters.collapse.show { + display: block; + position: absolute; + z-index: 20; + } + + .navbar-collapse { + max-height: none !important; + } +} + +.img-flag { + background-color: white; + border: 1px solid #ddd; + border-radius: 2px; + padding: 3px; + background-size: cover !important; + max-width: auto; + max-height: auto; + height: 19px !important; + width: 32px !important; + -webkit-box-shadow: 0 2px 10px rgba(0, 0, 0, .5), 0 2px 3px rgba(0, 0, 0, .5); + -moz-box-shadow: 0 2px 10px rgba(0, 0, 0, .5), 0 2px 3px rgba(0, 0, 0, .5); + -o-box-shadow: 0 2px 10px rgba(0, 0, 0, .5), 0 2px 3px rgba(0, 0, 0, .5); + box-shadow: 0 2px 10px rgba(0, 0, 0, .5), 0 2px 3px rgba(0, 0, 0, .5); +} + +.ipcs { + background-image: url("/static/images/background.webp"); + background-size: cover; + background-repeat: no-repeat; +} + +.copyleft { + display: inline-block; + transform: rotate(180deg); +} + +span.search-callsign { + background: url(/static/images/search-callsign.svg) no-repeat top left; + background-size: contain; + cursor: pointer; + display: inline-block; + height: 16px; + width: 20px; +} + +#input-group-callsign { + margin-right: 1rem; + margin-bottom: 0.5rem; +} + +#collapseFilters { + margin-top: 10px; + background-color: #DDE2E6; +} + +#spotsTable { + margin-top: 10px; +} + +#band { + margin-top: 5px; +} + +#dashboard { + padding: 10px; + gap: 10px; +} + +#telnet-thead { + position: sticky; + top: 0 +} + +#form-band_activity { + width: 600px; + height: 500px; +} + +#chart-band_activity { + width: 100%; + height: 400px +} + +#chart-world_dx_spots_live { + width: 600px; + height: 500px; + gap: 0px; +} + +#chart-hour_band { + width: 600px; + height: 500px; +} + +#silo-propagation-img { + width: 480px; + height: 400px; +} + +#chart-dx_spots_x_month { + width: 600px; + height: 400px; +} + +#chart-dx_spots_trend { + width: 700px; + height: 400px; +} \ No newline at end of file diff --git a/static/css/rel/README.md b/static/css/rel/README.md new file mode 100644 index 0000000..2442a5f --- /dev/null +++ b/static/css/rel/README.md @@ -0,0 +1,2 @@ +This folder `static/css/rel/` contains the style scripts for **release** environment. +You **CANNOT change** these scripts. Make changes only in `static/css/dev/` folder \ No newline at end of file diff --git a/static/css/rel/style.min.css b/static/css/rel/style.min.css new file mode 100644 index 0000000..7827269 --- /dev/null +++ b/static/css/rel/style.min.css @@ -0,0 +1 @@ +@import url("https://cdn.jsdelivr.net/npm/bootstrap-icons@1.10.1/font/bootstrap-icons.css");@font-face{font-display:swap;font-family:bootstrap-icons}.badge-responsive{width:70px}@media screen and (max-width:768px){.text-responsive{font-size:11px}.badge-responsive{width:40px}#collapseFilters.collapsing{position:absolute!important;z-index:20}#collapseFilters.collapse.show{display:block;position:absolute;z-index:20}.navbar-collapse{max-height:none!important}}.img-flag{background-color:#fff;background-size:cover!important;border:1px solid #ddd;border-radius:2px;-webkit-box-shadow:0 2px 10px rgba(0,0,0,.5),0 2px 3px rgba(0,0,0,.5);-moz-box-shadow:0 2px 10px rgba(0,0,0,.5),0 2px 3px rgba(0,0,0,.5);-o-box-shadow:0 2px 10px rgba(0,0,0,.5),0 2px 3px rgba(0,0,0,.5);box-shadow:0 2px 10px rgba(0,0,0,.5),0 2px 3px rgba(0,0,0,.5);height:19px!important;max-height:auto;max-width:auto;padding:3px;width:32px!important}.ipcs{background-image:url(/static/images/background.webp);background-repeat:no-repeat;background-size:cover}.copyleft{display:inline-block;transform:rotate(180deg)}span.search-callsign{background:url(/static/images/search-callsign.svg) no-repeat 0 0;background-size:contain;cursor:pointer;display:inline-block;height:16px;width:20px}#input-group-callsign{margin-bottom:.5rem;margin-right:1rem}#collapseFilters{background-color:#dde2e6;margin-top:10px}#spotsTable{margin-top:10px}#band{margin-top:5px}#dashboard{gap:10px;padding:10px}#telnet-thead{position:sticky;top:0}#form-band_activity{height:500px;width:600px}#chart-band_activity{height:400px;width:100%}#chart-world_dx_spots_live{gap:0;height:500px;width:600px}#chart-hour_band{height:500px;width:600px}#silo-propagation-img{height:400px;width:480px}#chart-dx_spots_x_month{height:400px;width:600px}#chart-dx_spots_trend{height:400px;width:700px} \ No newline at end of file diff --git a/static/css/style.css b/static/css/style.css deleted file mode 100644 index 8a7ca91..0000000 --- a/static/css/style.css +++ /dev/null @@ -1,128 +0,0 @@ -@import url("https://cdn.jsdelivr.net/npm/bootstrap-icons@1.10.1/font/bootstrap-icons.css"); - -@font-face { - font-display: swap; - font-family: 'bootstrap-icons'; - -} - -.badge-responsive { - width: 70px; -} - -@media screen and (max-width: 768px) { - .text-responsive { - font-size: 11px; - } - .badge-responsive { - width: 40px; - } -} - -@media screen and (max-width: 768px) { - #collapseFilters.collapsing { - position: absolute !important; - z-index: 20; - } - - #collapseFilters.collapse.show { - display: block; - position: absolute; - z-index: 20; - } - - .navbar-collapse { - max-height: none !important; - } -} - -.img-flag { - background-color: white; - border: 1px solid #ddd; - border-radius: 2px; - padding: 3px; - background-size:cover !important; - max-width: auto; - max-height: auto; - height: 19px !important; - width: 32px !important; - -webkit-box-shadow: 0 2px 10px rgba(0, 0, 0, .5), 0 2px 3px rgba(0, 0, 0, .5); - -moz-box-shadow: 0 2px 10px rgba(0, 0, 0, .5), 0 2px 3px rgba(0, 0, 0, .5); - -o-box-shadow: 0 2px 10px rgba(0, 0, 0, .5), 0 2px 3px rgba(0, 0, 0, .5); - box-shadow: 0 2px 10px rgba(0, 0, 0, .5), 0 2px 3px rgba(0, 0, 0, .5); -} - -.ipcs { - background-image: url("/static/images/background.webp"); - background-size: cover; - background-repeat: no-repeat; -} - -.copyleft { - display: inline-block; - transform: rotate(180deg); - } - -span.search-callsign { - background: url(/static/images/search-callsign.svg) no-repeat top left; - background-size: contain; - cursor: pointer; - display: inline-block; - height: 16px; - width: 20px; -} - -#input-group-callsign { - margin-right: 1rem; - margin-bottom: 0.5rem; -} - -#collapseFilters { - margin-top: 10px; - background-color: #DDE2E6; -} - -#spotsTable { - margin-top: 10px; -} - -#band { - margin-top: 5px; -} - -#dashboard { - padding:10px; - gap:10px; -} - -#telnet-thead { - position: sticky;top: 0 -} - -#form-band_activity { - width: 600px; height: 500px; -} - -#chart-band_activity { - width:100%; height:400px -} - -#chart-world_dx_spots_live { - width: 600px; height: 500px; gap: 0px; -} - -#chart-hour_band { - width: 600px; height: 500px; -} - -#silo-propagation-img { - width: 480px; height: 400px; -} - -#chart-dx_spots_x_month { - width: 600px; height: 400px; -} - -#chart-dx_spots_trend { - width: 700px; height: 400px; -} diff --git a/static/css/style.min.css b/static/css/style.min.css deleted file mode 100644 index 1b2fff0..0000000 --- a/static/css/style.min.css +++ /dev/null @@ -1 +0,0 @@ -@import url("https://cdn.jsdelivr.net/npm/bootstrap-icons@1.10.1/font/bootstrap-icons.css");@font-face{font-display:swap;font-family:'bootstrap-icons'}.badge-responsive{width:70px}@media screen and (max-width:768px){.text-responsive{font-size:11px}.badge-responsive{width:40px}}@media screen and (max-width:768px){#collapseFilters.collapsing{position:absolute!important;z-index:20}#collapseFilters.collapse.show{display:block;position:absolute;z-index:20}.navbar-collapse{max-height:none!important}}.img-flag{background-color:white;border:1px solid #ddd;border-radius:2px;padding:3px;background-size:cover!important;max-width:auto;max-height:auto;height:19px!important;width:32px!important;-webkit-box-shadow:0 2px 10px rgba(0,0,0,.5),0 2px 3px rgba(0,0,0,.5);-moz-box-shadow:0 2px 10px rgba(0,0,0,.5),0 2px 3px rgba(0,0,0,.5);-o-box-shadow:0 2px 10px rgba(0,0,0,.5),0 2px 3px rgba(0,0,0,.5);box-shadow:0 2px 10px rgba(0,0,0,.5),0 2px 3px rgba(0,0,0,.5)}.ipcs{background-image:url("/static/images/background.webp");background-size:cover;background-repeat:no-repeat}.copyleft{display:inline-block;transform:rotate(180deg)}span.search-callsign{background:url(/static/images/search-callsign.svg) no-repeat top left;background-size:contain;cursor:pointer;display:inline-block;height:16px;width:20px}#input-group-callsign{margin-right:1rem;margin-bottom:.5rem}#collapseFilters{margin-top:10px;background-color:#dde2e6}#spotsTable{margin-top:10px}#band{margin-top:5px}#dashboard{padding:10px;gap:10px}#telnet-thead{position:sticky;top:0}#form-band_activity{width:600px;height:500px}#chart-band_activity{width:100%;height:400px}#chart-world_dx_spots_live{width:600px;height:500px;gap:0}#chart-hour_band{width:600px;height:500px}#silo-propagation-img{width:480px;height:400px}#chart-dx_spots_x_month{width:600px;height:400px}#chart-dx_spots_trend{width:700px;height:400px} \ No newline at end of file diff --git a/cfg/cty_wt_mod.dat b/static/data/cty_wt_mod.dat similarity index 94% rename from cfg/cty_wt_mod.dat rename to static/data/cty_wt_mod.dat index 535f438..a806594 100644 --- a/cfg/cty_wt_mod.dat +++ b/static/data/cty_wt_mod.dat @@ -1,6 +1,6 @@ # -# RELEASE 2022.12.22.00 -# based on CTY-3240 +# RELEASE 2023.01.06.00 +# based on CTY-3302 # # ADIF 246 Sov Mil Order of Malta: 15: 28: EU: 41.90: -12.43: -1.0: 1A: @@ -43,7 +43,7 @@ Kingdom of Eswatini: 38: 57: AF: -26.65: -31.48: -2.0: 3DA: 3DA; # ADIF 474 Tunisia: 33: 37: AF: 35.40: -9.32: -1.0: 3V: - 3V,TS; + 3V,TS,=3V8CB/YOTA; # ADIF 293 Vietnam: 26: 49: AS: 15.80: -107.90: -7.0: 3W: 3W,XV; @@ -52,7 +52,7 @@ Guinea: 35: 46: AF: 11.00: 10.68: 0.0: 3X: 3X; # ADIF 24 Bouvet: 38: 67: AF: -54.42: -3.38: -1.0: 3Y/b: - =3Y0J; + =3Y/LB1QI,=3Y/LB5GI,=3Y0J,=3Y7GIA,=3Y7THA; # ADIF 199 Peter 1 Island: 12: 72: SA: -68.77: 90.58: 4.0: 3Y/p: =3Y0X; @@ -76,7 +76,7 @@ United Nations HQ: 05: 08: NA: 40.75: 73.97: 5.0: 4U1U: =4U1UN; # ADIF 206 Vienna Intl Ctr: 15: 28: EU: 48.20: -16.30: -1.0: *4U1V: - =4U0R,=4U1A,=4U1VIC,=4U1XMAS,=4U2U,=4Y1A,=C7A; + =4U0R,=4U1A,=4U1VIC,=4U2U,=4Y1A,=C7A; # ADIF 511 Timor - Leste: 28: 54: OC: -8.80: -126.05: -9.0: 4W: 4W; @@ -409,7 +409,7 @@ China: 24: 44: AS: 36.00: -102.00: -8.0: BY: 3R1<39.93/-116.40>,3S1<39.93/-116.40>,3T1<39.93/-116.40>,3U1<39.93/-116.40>,B1<39.93/-116.40>, BA1<39.93/-116.40>,BD1<39.93/-116.40>,BG1<39.93/-116.40>,BH1<39.93/-116.40>,BI1<39.93/-116.40>, BJ1<39.93/-116.40>,BL1<39.93/-116.40>,BT1<39.93/-116.40>,BY1<39.93/-116.40>,BZ1<39.93/-116.40>, - XS1<39.93/-116.40>, + XS1<39.93/-116.40>,=BY1CRA/WRTC<39.93/-116.40>, # Hei Long Jiang 3H2A[33]<45.80/-126.67>,3H2B[33]<45.80/-126.67>,3H2C[33]<45.80/-126.67>,3H2D[33]<45.80/-126.67>, 3H2E[33]<45.80/-126.67>,3H2F[33]<45.80/-126.67>,3H2G[33]<45.80/-126.67>,3H2H[33]<45.80/-126.67>, @@ -1841,9 +1841,7 @@ Antarctica: 13: 74: SA: -90.00: 0.00: 0.0: CE9: # Neumayer III Station =DP0GVN(38)[67]<-70.67/8.27>,=DP1POL(38)[67]<-70.67/8.27>, # Halley VI Station - =VP8/SQ1SGB[73]<-75.62/26.13>, -# Wolf's Fang Runway - =VP8/SQ1SGB/P(38)[67]<-71.52/-8.80>; + =VP8/SQ1SGB[73]<-75.62/26.13>; # ADIF 70 Cuba: 08: 11: NA: 21.50: 80.00: 5.0: CM: CL,CM,CO,T4, @@ -2134,7 +2132,7 @@ Spain: 14: 37: EU: 40.32: 3.43: -1.0: EA: # AV-BU-C-LE-LO-LU-O-OU-P-PO-S-SA-SG-SO-VA-ZA AM1<42.57/6.17>,AN1<42.57/6.17>,AO1<42.57/6.17>,EA1<42.57/6.17>,EB1<42.57/6.17>,EC1<42.57/6.17>, ED1<42.57/6.17>,EE1<42.57/6.17>,EF1<42.57/6.17>,EG1<42.57/6.17>,EH1<42.57/6.17>, - =EA1RCI/CVG<42.57/6.17>,=EA6XQ/P<42.57/6.17>, + =EA1RCI/CVG<42.57/6.17>, # BI-HU-NA-SS-TE-VI-Z AM2<42.60/1.93>,AN2<42.60/1.93>,AO2<42.60/1.93>,EA2<42.60/1.93>,EB2<42.60/1.93>,EC2<42.60/1.93>, ED2<42.60/1.93>,EE2<42.60/1.93>,EF2<42.60/1.93>,EG2<42.60/1.93>,EH2<42.60/1.93>, @@ -2146,10 +2144,11 @@ Spain: 14: 37: EU: 40.32: 3.43: -1.0: EA: # BA-CC-CR-CU-GU-M-TO AM4<40.18/3.93>,AN4<40.18/3.93>,AO4<40.18/3.93>,EA4<40.18/3.93>,EB4<40.18/3.93>,EC4<40.18/3.93>, ED4<40.18/3.93>,EE4<40.18/3.93>,EF4<40.18/3.93>,EG4<40.18/3.93>,EH4<40.18/3.93>, + =EA6XQ/P<40.18/3.93>, # A-AB-CS-MU-V AM5<38.87/0.97>,AN5<38.87/0.97>,AO5<38.87/0.97>,EA5<38.87/0.97>,EB5<38.87/0.97>,EC5<38.87/0.97>, ED5<38.87/0.97>,EE5<38.87/0.97>,EF5<38.87/0.97>,EG5<38.87/0.97>,EH5<38.87/0.97>, - =EA5CC/P<38.87/0.97>,=EA5IKT/P<38.87/0.97>,=EC5AHA/P<38.87/0.97>, + =EA5CC/P<38.87/0.97>,=EA5IKT/P<38.87/0.97>,=EA9AZ/5<38.87/0.97>,=EC5AHA/P<38.87/0.97>, # AL-CA-CO-GR-H-J-MA-SE AM7<37.25/4.68>,AN7<37.25/4.68>,AO7<37.25/4.68>,EA7<37.25/4.68>,EB7<37.25/4.68>,EC7<37.25/4.68>, ED7<37.25/4.68>,EE7<37.25/4.68>,EF7<37.25/4.68>,EG7<37.25/4.68>,EH7<37.25/4.68>, @@ -2240,7 +2239,7 @@ Kyrgyzstan: 17: 30: AS: 41.70: -74.13: -6.0: EX: EX0V<40.93/-73.00>,EX2V<40.93/-73.00>,EX6V<40.93/-73.00>,EX7V<40.93/-73.00>,EX8V<40.93/-73.00>; # ADIF 262 Tajikistan: 17: 30: AS: 38.82: -71.22: -5.0: EY: - EY; + EY,=VERSION; # ADIF 280 Turkmenistan: 17: 30: AS: 38.00: -58.00: -5.0: EZ: EZ, @@ -2294,7 +2293,7 @@ St. Pierre & Miquelon: 05: 09: NA: 46.77: 56.20: 3.0: FP: FP; # ADIF 453 Reunion Island: 39: 53: AF: -21.12: -55.48: -4.0: FR: - FR; + FR,=TO974QO; # ADIF 213 St. Martin: 08: 11: NA: 18.08: 63.03: 4.0: FS: FS,=TO5T,=TO9W; @@ -2335,14 +2334,14 @@ Isle of Man: 14: 27: EU: 54.20: 4.53: 0.0: GD: 2D,GD,GT,MD,MT; # ADIF 265 Northern Ireland: 14: 27: EU: 54.73: 6.68: 0.0: GI: - 2I,GI,GN,MI,MN,=GB0KC,=GB0QE,=GB8LWF,=GB8PJE; + 2I,GI,GN,MI,MN,=GB0KC,=GB0QE,=GB8ELF,=GB8LWF,=GB8PJE; # ADIF 122 Jersey: 14: 27: EU: 49.22: 2.18: 0.0: GJ: 2J,GH,GJ,MH,MJ; # ADIF 279 Scotland: 14: 27: EU: 56.82: 4.18: 0.0: GM: - 2A,2M,GM,GS,MA,MM,MS,=GB0SSB,=GB2ELH,=GB2JCM,=GB2OWM,=GB3ANG,=GB3LER,=GB3LER/B,=GB3ORK,=GB3RMK/B, - =GB4LER; + 2A,2M,GM,GS,MA,MM,MS,=GB0SSB,=GB2ELH,=GB2JCM,=GB2KW,=GB2OWM,=GB3ANG,=GB3LER,=GB3LER/B,=GB3ORK, + =GB3RMK/B,=GB4LER; # ADIF 279 Shetland Islands: 14: 27: EU: 60.50: 1.50: 0.0: *GM/s: =2M0BDR,=2M0BDT,=2M0CPN,=2M0GFC,=2M0SEG,=2M0SPX,=2M0ZET,=GB2ELH,=GB3LER,=GB3LER/B,=GB4LER,=GM0AVR, @@ -2356,7 +2355,7 @@ Guernsey: 14: 27: EU: 49.45: 2.58: 0.0: GU: 2U,GP,GU,MP,MU; # ADIF 294 Wales: 14: 27: EU: 52.28: 3.73: 0.0: GW: - 2W,GC,GW,MC,MW,=GB0MPA,=GB1BAF,=GB1MUU,=GB2IMD,=GB2TD,=GB2WFC,=GB4MDI; + 2W,GC,GW,MC,MW,=GB0MPA,=GB2IMD,=GB2TD,=GB2WFC,=GB4MDI; # ADIF 185 Solomon Islands: 28: 51: OC: -9.00: -160.00: -11.0: H4: H4; @@ -2522,7 +2521,7 @@ Italy: 15: 28: EU: 42.82: -12.58: -1.0: I: # Lazio/Umbria I0<41.88/-12.53>,II0<41.88/-12.53>,IK0<41.88/-12.53>,IO0<41.88/-12.53>,IQ0<41.88/-12.53>, IR0<41.88/-12.53>,IU0<41.88/-12.53>,IW0<41.88/-12.53>,IZ0<41.88/-12.53>,=IT9ELM/0<41.88/-12.53>, - =IT9PQJ/0<41.88/-12.53>, + =IT9HJH/0<41.88/-12.53>,=IT9PQJ/0<41.88/-12.53>, # Liguria/Piemonte I1<45.07/-7.67>,II1<45.07/-7.67>,IK1<45.07/-7.67>,IO1<45.07/-7.67>,IQ1<45.07/-7.67>, IR1<45.07/-7.67>,IU1<45.07/-7.67>,IW1<45.07/-7.67>,IZ1<45.07/-7.67>, @@ -2576,8 +2575,8 @@ African Italy: 33: 37: AF: 35.67: -12.67: -1.0: *IG9: IG9,IH9; # ADIF 225 Sardinia: 15: 28: EU: 40.15: -9.27: -1.0: IS: - IM0,IS,IW0U,IW0V,IW0W,IW0X,IW0Y,IW0Z,=II0ICH,=II0IDP,=II0M,=IQ0AG,=IQ0AH,=IQ0AH/P,=IQ0AI,=IQ0AK, - =IQ0AL,=IQ0AM,=IQ0EH,=IQ0HO,=IQ0ID,=IQ0NU,=IQ0OG,=IQ0QP,=IQ0SS,=IY0GA; + IM0,IS,IW0U,IW0V,IW0W,IW0X,IW0Y,IW0Z,=II0ICH,=II0IDP,=II0M,=IO0WRTC,=IQ0AAI,=IQ0AG,=IQ0AH, + =IQ0AH/P,=IQ0AI,=IQ0AK,=IQ0AL,=IQ0AM,=IQ0EH,=IQ0HO,=IQ0ID,=IQ0NU,=IQ0OG,=IQ0QP,=IQ0SS,=IY0GA; # ADIF 248 Sicily: 15: 28: EU: 37.50: -14.00: -1.0: *IT9: IB9,ID9,IE9,IF9,II9,IJ9,IO9,IQ9,IR9,IT9,IU9,IW9,IY9,=II1MM/9,=IQ1QQ/9,=IT9ACJ/I/BO,=IW0HBY/9; @@ -2727,19 +2726,21 @@ United States: 05: 08: NA: 37.60: 91.87: 5.0: K: WW0(4)[7]<41.12/96.57>~6.0~,WX0(4)[7]<41.12/96.57>~6.0~,WY0(4)[7]<41.12/96.57>~6.0~, WZ0(4)[7]<41.12/96.57>~6.0~,=AB4BA(4)[7]<39.53/105.18>~6.0~,=AD1C(4)[7]<39.53/105.18>~6.0~, =AI6O(4)[7]<38.43/92.23>~6.0~,=AI7C(4)[7]<39.53/105.18>~6.0~,=AK9D(4)[7]<38.48/96.42>~6.0~, - =AL0G(4)[7]<39.53/105.18>~6.0~,=K1KD(4)[7]<45.18/93.57>~6.0~,=K1WTF(4)[7]<39.53/105.18>~6.0~, + =AL0G(4)[7]<39.53/105.18>~6.0~,=K1EAR(4)[7]<43.98/98.92>~6.0~,=K1KD(4)[7]<45.18/93.57>~6.0~, + =K1QS(4)[7]<38.43/92.23>~6.0~,=K1WTF(4)[7]<39.53/105.18>~6.0~,=K1XT(4)[7]<38.43/92.23>~6.0~, =K2KR(4)[7]<39.53/105.18>~6.0~,=K2VV(4)[7]<38.43/92.23>~6.0~,=K3ILC(4)[7]<39.53/105.18>~6.0~, =K3PA(4)[7]<38.48/96.42>~6.0~,=K4EQ(4)[7]<38.43/92.23>~6.0~,=K4IU(4)[7]<45.18/93.57>~6.0~, - =K4RVA(4)[7]<39.53/105.18>~6.0~,=K7BG(4)[7]<43.98/98.92>~6.0~,=K7SCX(4)[7]<39.53/105.18>~6.0~, - =K7TD(4)[7]<39.53/105.18>~6.0~,=K9DU(4)[7]<45.18/93.57>~6.0~,=K9MWM(4)[7]<39.53/105.18>~6.0~, - =K9OR(4)[7]<39.53/105.18>~6.0~,=KA4GAV(4)[7]<45.18/93.57>~6.0~,=KA9OZP(4)[7]<41.93/93.03>~6.0~, - =KB3HF(4)[7]<38.43/92.23>~6.0~,=KB4IRR(4)[7]<38.43/92.23>~6.0~,=KB9LLD(4)[7]<38.43/92.23>~6.0~, - =KC6R(4)[7]<39.53/105.18>~6.0~,=KC8NFN(4)[7]<47.33/99.45>~6.0~,=KD7TO(4)[7]<45.18/93.57>~6.0~, - =KF6YU(4)[7]<39.53/105.18>~6.0~,=KF7MD(4)[7]<39.53/105.18>~6.0~,=KF9MT(4)[7]<41.93/93.03>~6.0~, - =KK9N(4)[7]<38.43/92.23>~6.0~,=KL7GLK(4)[7]<39.53/105.18>~6.0~,=KL7NW(4)[7]<41.17/97.22>~6.0~, - =KL7QW(4)[7]<38.43/92.23>~6.0~,=KO1Z(4)[7]<45.18/93.57>~6.0~,=KO9V(4)[7]<39.53/105.18>~6.0~, - =KP2X(4)[7]<38.43/92.23>~6.0~,=KS9W(4)[7]<39.53/105.18>~6.0~,=KT3M(4)[7]<39.53/105.18>~6.0~, - =KT9T(4)[7]<45.18/93.57>~6.0~,=KW7Q(4)[7]<39.53/105.18>~6.0~,=KX2P(4)[7]<39.53/105.18>~6.0~, + =K4RVA(4)[7]<39.53/105.18>~6.0~,=K6DJ(4)[7]<39.53/105.18>~6.0~,=K7BG(4)[7]<43.98/98.92>~6.0~, + =K7SCX(4)[7]<39.53/105.18>~6.0~,=K7TD(4)[7]<39.53/105.18>~6.0~,=K9DU(4)[7]<45.18/93.57>~6.0~, + =K9MWM(4)[7]<39.53/105.18>~6.0~,=K9OR(4)[7]<39.53/105.18>~6.0~,=KA3NAM(4)[7]<38.48/96.42>~6.0~, + =KA4GAV(4)[7]<45.18/93.57>~6.0~,=KA9OZP(4)[7]<41.93/93.03>~6.0~,=KB3HF(4)[7]<38.43/92.23>~6.0~, + =KB4IRR(4)[7]<38.43/92.23>~6.0~,=KB9LLD(4)[7]<38.43/92.23>~6.0~,=KC6R(4)[7]<39.53/105.18>~6.0~, + =KC8NFN(4)[7]<47.33/99.45>~6.0~,=KD7TO(4)[7]<45.18/93.57>~6.0~,=KF6YU(4)[7]<39.53/105.18>~6.0~, + =KF7MD(4)[7]<39.53/105.18>~6.0~,=KF9MT(4)[7]<41.93/93.03>~6.0~,=KK9N(4)[7]<38.43/92.23>~6.0~, + =KL7GLK(4)[7]<39.53/105.18>~6.0~,=KL7NW(4)[7]<41.17/97.22>~6.0~,=KL7QW(4)[7]<38.43/92.23>~6.0~, + =KO1Z(4)[7]<45.18/93.57>~6.0~,=KO9V(4)[7]<39.53/105.18>~6.0~,=KP2X(4)[7]<38.43/92.23>~6.0~, + =KS9W(4)[7]<39.53/105.18>~6.0~,=KT3M(4)[7]<39.53/105.18>~6.0~,=KT9T(4)[7]<45.18/93.57>~6.0~, + =KW7Q(4)[7]<39.53/105.18>~6.0~,=KX2P(4)[7]<39.53/105.18>~6.0~,=N1CCC(4)[7]<38.43/92.23>~6.0~, =N1SMB(4)[7]<39.53/105.18>~6.0~,=N2DEE(4)[7]<39.53/105.18>~6.0~,=N4VI(4)[7]<39.53/105.18>~6.0~, =N6RSH(4)[7]<41.17/97.22>~6.0~,=N6WHV(4)[7]<39.53/105.18>~6.0~,=N7DR(4)[7]<39.53/105.18>~6.0~, =N7IV(4)[7]<47.33/99.45>~6.0~,=N7SE(4)[7]<39.53/105.18>~6.0~,=N8HOQ(4)[7]<38.43/92.23>~6.0~, @@ -2752,8 +2753,9 @@ United States: 05: 08: NA: 37.60: 91.87: 5.0: K: =W7RF(4)[7]<39.53/105.18>~6.0~,=W7RY(4)[7]<38.43/92.23>~6.0~,=W7UM(4)[7]<39.53/105.18>~6.0~, =W7UT(4)[7]<43.98/98.92>~6.0~,=W8LYJ(4)[7]<39.53/105.18>~6.0~,=W8XAL(4)[7]<39.53/105.18>~6.0~, =W9MAF(4)[7]<38.43/92.23>~6.0~,=WA1FMM(4)[7]<39.53/105.18>~6.0~,=WA2JQZ(4)[7]<39.53/105.18>~6.0~, - =WA2PCN(4)[7]<39.53/105.18>~6.0~,=WB7BNE(4)[7]<41.93/93.03>~6.0~,=WI4T(4)[7]<38.43/92.23>~6.0~, - =WO7U(4)[7]<38.43/92.23>~6.0~,=WR2JN(4)[7]<39.53/105.18>~6.0~,=WT7TT(4)[7]<39.53/105.18>~6.0~, + =WB7BNE(4)[7]<41.93/93.03>~6.0~,=WB9YLZ(4)[7]<45.18/93.57>~6.0~,=WD1W(4)[7]<39.53/105.18>~6.0~, + =WD4IXD(4)[7]<39.53/105.18>~6.0~,=WI4T(4)[7]<38.43/92.23>~6.0~,=WO7U(4)[7]<38.43/92.23>~6.0~, + =WR2JN(4)[7]<39.53/105.18>~6.0~,=WT7TT(4)[7]<39.53/105.18>~6.0~, # CT-MA-ME-NH-RI-VT AA1<42.38/71.65>,AB1<42.38/71.65>,AC1<42.38/71.65>,AD1<42.38/71.65>,AE1<42.38/71.65>, AF1<42.38/71.65>,AG1<42.38/71.65>,AI1<42.38/71.65>,AJ1<42.38/71.65>,AK1<42.38/71.65>, @@ -2773,9 +2775,9 @@ United States: 05: 08: NA: 37.60: 91.87: 5.0: K: WS1<42.38/71.65>,WT1<42.38/71.65>,WU1<42.38/71.65>,WV1<42.38/71.65>,WW1<42.38/71.65>, WX1<42.38/71.65>,WY1<42.38/71.65>,WZ1<42.38/71.65>,=AE6JV<43.15/71.45>,=K0HNL<42.27/71.35>, =K5ZD<42.27/71.35>,=K8CN<43.15/71.45>,=K9JY<44.10/72.82>,=KH7Z<42.27/71.35>,=KL3VA<44.27/69.77>, - =KR5X<43.15/71.45>,=N7ECM<43.15/71.45>,=N8RA<41.50/72.88>,=N9NC<43.15/71.45>,=NP2GG<41.50/72.88>, - =W0JAY<41.50/72.88>,=W8HAP<44.27/69.77>,=W8HMK<41.75/71.45>,=WB8IMY<41.50/72.88>, - =WB8PKK<42.27/71.35>,=WX7T<41.50/72.88>, + =KR5X<43.15/71.45>,=N7ECM<43.15/71.45>,=N8RA<41.50/72.88>,=N9NC<43.15/71.45>,=NL7FJ<42.27/71.35>, + =NP2GG<41.50/72.88>,=W0JAY<41.50/72.88>,=W8HAP<44.27/69.77>,=W8HMK<41.75/71.45>, + =WB8IMY<41.50/72.88>,=WB8PKK<42.27/71.35>,=WM0G<43.15/71.45>,=WX7T<41.50/72.88>, # NJ-NY AA2<41.15/74.53>,AB2<41.15/74.53>,AC2<41.15/74.53>,AD2<41.15/74.53>,AE2<41.15/74.53>, AF2<41.15/74.53>,AG2<41.15/74.53>,AI2<41.15/74.53>,AJ2<41.15/74.53>,AK2<41.15/74.53>, @@ -2793,9 +2795,10 @@ United States: 05: 08: NA: 37.60: 91.87: 5.0: K: WG2<41.15/74.53>,WI2<41.15/74.53>,WJ2<41.15/74.53>,WK2<41.15/74.53>,WM2<41.15/74.53>, WN2<41.15/74.53>,WO2<41.15/74.53>,WQ2<41.15/74.53>,WR2<41.15/74.53>,WS2<41.15/74.53>, WT2<41.15/74.53>,WU2<41.15/74.53>,WV2<41.15/74.53>,WW2<41.15/74.53>,WX2<41.15/74.53>, - WY2<41.15/74.53>,WZ2<41.15/74.53>,=AH2O<41.47/74.58>,=K9CHP<41.47/74.58>,=KO8SCA<41.47/74.58>, - =KP4AK<41.47/74.58>,=N5DX<41.15/74.53>,=NY6DX<41.47/74.58>,=W0MHK<40.43/74.42>, - =W6JTB<41.47/74.58>,=WS9M<41.47/74.58>, + WY2<41.15/74.53>,WZ2<41.15/74.53>,=AH2O<41.47/74.58>,=K0SM<41.47/74.58>,=K9CHP<41.47/74.58>, + =KE5HDE<41.47/74.58>,=KO8SCA<41.47/74.58>,=KP4AK<41.47/74.58>,=N5DX<41.15/74.53>, + =NX7U<40.43/74.42>,=NY6DX<41.47/74.58>,=W0MHK<40.43/74.42>,=W6JTB<41.47/74.58>, + =W8NUD<41.47/74.58>,=WS9M<41.47/74.58>, # DE-MD-PA AA3<39.95/76.85>,AB3<39.95/76.85>,AC3<39.95/76.85>,AD3<39.95/76.85>,AE3<39.95/76.85>, AF3<39.95/76.85>,AG3<39.95/76.85>,AI3<39.95/76.85>,AJ3<39.95/76.85>,AK3<39.95/76.85>, @@ -2838,42 +2841,43 @@ United States: 05: 08: NA: 37.60: 91.87: 5.0: K: WT4<33.12/82.20>,WU4<33.12/82.20>,WV4<33.12/82.20>,WW4<33.12/82.20>,WX4<33.12/82.20>, WY4<33.12/82.20>,WZ4<33.12/82.20>,=AA0O<27.83/81.63>,=AA5JF<33.42/83.88>,=AA5UN<27.83/81.63>, =AA8R<27.83/81.63>,=AA9HQ<34.02/81.00>,=AC6WI<37.85/77.77>,=AC7GG<34.02/81.00>,=AC8Y<37.85/77.77>, - =AD8J<35.53/79.68>,=AH6R<27.83/81.63>,=AI8O<35.53/79.68>,=G8ERJ<33.12/82.20>,=K0CIE<27.83/81.63>, - =K0DSL<27.83/81.63>,=K0FE<34.02/81.00>,=K0HT<33.42/83.88>,=K0LUZ<27.83/81.63>,=K0ZR<37.85/77.77>, - =K5EK<35.53/79.68>,=K5JR<33.42/83.88>,=K5KG<27.83/81.63>,=K5OF<35.53/79.68>,=K5TF<33.42/83.88>, - =K5VG<37.85/77.77>,=K5VIP<37.85/77.77>,=K6ES<35.53/79.68>,=K6RM<35.53/79.68>,=K7BV<35.53/79.68>, - =K7GM<35.53/79.68>,=K7LU<37.85/77.77>,=K7OM<34.02/81.00>,=K7SV<37.85/77.77>,=K7UPJ<27.83/81.63>, - =K7UWR<33.42/83.88>,=K7WCH<34.02/81.00>,=K8AC<35.53/79.68>,=K8ARY<34.02/81.00>,=K8FC<27.83/81.63>, - =K8GP<37.85/77.77>,=K8LBQ<33.42/83.88>,=K8LF<37.85/77.77>,=K8MV<27.83/81.63>,=K8NS<27.83/81.63>, - =K8RAR<27.83/81.63>,=K8SYH<37.85/77.77>,=K8YC<35.53/79.68>,=K8YFM<37.85/77.77>, - =K9AXT<35.53/79.68>,=K9EZ<35.53/79.68>,=K9GS<27.83/81.63>,=K9GWS<34.02/81.00>,=K9OM<27.83/81.63>, - =K9RQ<27.83/81.63>,=KA8OAT<27.83/81.63>,=KA8P<34.02/81.00>,=KA8Q<37.85/77.77>, - =KC8GCR<35.53/79.68>,=KC8OPV<37.85/77.77>,=KF7RO<34.02/81.00>,=KF8N<37.85/77.77>, - =KG9V<34.02/81.00>,=KH2D<27.83/81.63>,=KH2PM<35.53/79.68>,=KH6CT<27.83/81.63>,=KH6M<27.83/81.63>, - =KH7DM<35.53/79.68>,=KH7FC<37.85/77.77>,=KK5RX<33.42/83.88>,=KK9A<35.53/79.68>,=KK9O<27.83/81.63>, - =KL0SS<37.85/77.77>,=KL7JR<34.02/81.00>,=KN6BU<37.85/77.77>,=KO8V<37.85/77.77>,=KP2L<34.02/81.00>, - =KP3U<34.02/81.00>,=KP4Q<27.83/81.63>,=KS7X<27.83/81.63>,=KT6D<35.53/79.68>,=KU8E<33.42/83.88>, - =KZ5ED<35.53/79.68>,=N0FIR<37.85/77.77>,=N0MU<27.83/81.63>,=N0OJ<27.83/81.63>,=N0SMX<27.83/81.63>, - =N0VFJ<27.83/81.63>,=N5BO<27.83/81.63>,=N5GF<27.83/81.63>,=N5SMQ<37.85/77.77>,=N5TOO<33.42/83.88>, - =N5VX<35.53/79.68>,=N6AR<27.83/81.63>,=N6DW<37.85/77.77>,=N6NT<35.53/79.68>,=N6YMM<34.02/81.00>, - =N7RCS<27.83/81.63>,=N8AID<37.85/77.77>,=N8CL<27.83/81.63>,=N8GU<27.83/81.63>,=N8IK<37.85/77.77>, - =N8KH<27.83/81.63>,=N8NN<27.83/81.63>,=N8RW<27.83/81.63>,=N9EE<27.83/81.63>,=N9PGG<35.53/79.68>, - =NA9J<27.83/81.63>,=ND8L<27.83/81.63>,=NE8P<27.83/81.63>,=NF6P<27.83/81.63>,=NI7R<34.02/81.00>, - =NJ8J<33.42/83.88>,=NK7U<27.83/81.63>,=NL7P<37.85/77.77>,=NL7WA<27.83/81.63>,=NN7CW<27.83/81.63>, - =NN9DD<33.42/83.88>,=NO9E<33.42/83.88>,=NP2D<27.83/81.63>,=NP2JV<27.83/81.63>,=NP3G<27.83/81.63>, - =NP3K<27.83/81.63>,=NP3ST<27.83/81.63>,=NP4CV<27.83/81.63>,=NT0K<35.53/79.68>,=NX9T<35.53/79.68>, - =W0LL<27.83/81.63>,=W0NA<37.85/77.77>,=W0PV<27.83/81.63>,=W5FB<37.85/77.77>,=W5TAF<37.85/77.77>, - =W5VS<37.85/77.77>,=W6DVS<35.53/79.68>,=W6HGF<37.85/77.77>,=W6KW<33.42/83.88>,=W6NWS<35.53/79.68>, - =W7GKF<27.83/81.63>,=W7HJ<37.85/77.77>,=W7IMP<27.83/81.63>,=W7IY<37.85/77.77>,=W8BLA<33.42/83.88>, - =W8BRY<37.85/77.77>,=W8BT<37.85/77.77>,=W8FN<35.53/79.68>,=W8IX<27.83/81.63>,=W8KRZ<37.85/77.77>, - =W8WZ<35.53/79.68>,=W8XC<33.42/83.88>,=W8ZN<37.85/77.77>,=W9BS<27.83/81.63>,=W9DC<34.02/81.00>, - =W9GOL<27.83/81.63>,=W9KB<27.83/81.63>,=W9MET<27.83/81.63>,=W9TCV<27.83/81.63>, - =WA0LJM<33.42/83.88>,=WA8AHZ<37.85/77.77>,=WA8OJR<34.02/81.00>,=WA8YVF<35.53/79.68>, - =WB0POH<37.85/77.77>,=WB5NHL<34.02/81.00>,=WB5RYB<33.42/83.88>,=WB7PMP<35.53/79.68>, - =WB8CZH<27.83/81.63>,=WB8PAF<27.83/81.63>,=WC0W<27.83/81.63>,=WD9GKG<27.83/81.63>, - =WE8R<27.83/81.63>,=WE8USN<37.85/77.77>,=WF7I<37.85/77.77>,=WF8E<33.42/83.88>,=WF9A<33.42/83.88>, - =WH6A<27.83/81.63>,=WH6AQ<37.85/77.77>,=WH6LE<35.53/79.68>,=WN7S<37.85/77.77>,=WP2C<27.83/81.63>, - =WP2J<27.83/81.63>,=WP3ME<27.83/81.63>,=WP4BJS<34.02/81.00>,=WS6X<37.85/77.77>,=WS7X<27.83/81.63>, + =AD8J<35.53/79.68>,=AH6R<27.83/81.63>,=AI8F<33.42/83.88>,=AI8O<35.53/79.68>,=G8ERJ<33.12/82.20>, + =K0CIE<27.83/81.63>,=K0DSL<27.83/81.63>,=K0FE<34.02/81.00>,=K0HT<33.42/83.88>,=K0LUZ<27.83/81.63>, + =K0ZR<37.85/77.77>,=K5EK<35.53/79.68>,=K5JR<33.42/83.88>,=K5KG<27.83/81.63>,=K5OF<35.53/79.68>, + =K5TF<33.42/83.88>,=K5VG<37.85/77.77>,=K5VIP<37.85/77.77>,=K6ES<35.53/79.68>,=K6RM<35.53/79.68>, + =K7BV<35.53/79.68>,=K7GM<35.53/79.68>,=K7LU<37.85/77.77>,=K7OM<34.02/81.00>,=K7SV<37.85/77.77>, + =K7UPJ<27.83/81.63>,=K7UWR<33.42/83.88>,=K7WCH<34.02/81.00>,=K8AC<35.53/79.68>, + =K8ARY<34.02/81.00>,=K8FC<27.83/81.63>,=K8GP<37.85/77.77>,=K8LBQ<33.42/83.88>,=K8LF<37.85/77.77>, + =K8MV<27.83/81.63>,=K8NS<27.83/81.63>,=K8RAR<27.83/81.63>,=K8SYH<37.85/77.77>,=K8YC<35.53/79.68>, + =K8YFM<37.85/77.77>,=K9AXT<35.53/79.68>,=K9EZ<35.53/79.68>,=K9GS<27.83/81.63>,=K9GWS<34.02/81.00>, + =K9HXO<27.83/81.63>,=K9OM<27.83/81.63>,=K9RQ<27.83/81.63>,=KA8OAT<27.83/81.63>,=KA8P<34.02/81.00>, + =KA8Q<37.85/77.77>,=KC8GCR<35.53/79.68>,=KC8OPV<37.85/77.77>,=KD7ACB<27.83/81.63>, + =KF7RO<34.02/81.00>,=KF8N<37.85/77.77>,=KG9V<34.02/81.00>,=KH2D<27.83/81.63>,=KH2PM<35.53/79.68>, + =KH6CT<27.83/81.63>,=KH6M<27.83/81.63>,=KH7DM<35.53/79.68>,=KH7FC<37.85/77.77>,=KK9A<35.53/79.68>, + =KK9O<27.83/81.63>,=KL0SS<37.85/77.77>,=KL4C<33.42/83.88>,=KM5AT<27.83/81.63>,=KN6BU<37.85/77.77>, + =KO8V<37.85/77.77>,=KP2L<34.02/81.00>,=KP3U<34.02/81.00>,=KP4Q<27.83/81.63>,=KS7X<27.83/81.63>, + =KT6D<35.53/79.68>,=KU8E<33.42/83.88>,=KZ5ED<35.53/79.68>,=N0FIR<37.85/77.77>,=N0MU<27.83/81.63>, + =N0OJ<27.83/81.63>,=N0SMX<27.83/81.63>,=N0VFJ<27.83/81.63>,=N5BO<27.83/81.63>,=N5GF<27.83/81.63>, + =N5SMQ<37.85/77.77>,=N5TOO<33.42/83.88>,=N5VX<35.53/79.68>,=N6AQ<37.85/77.77>,=N6AR<27.83/81.63>, + =N6DW<37.85/77.77>,=N6NT<35.53/79.68>,=N6YMM<34.02/81.00>,=N7RCS<27.83/81.63>,=N8AID<37.85/77.77>, + =N8CL<27.83/81.63>,=N8CQD<35.53/79.68>,=N8GU<27.83/81.63>,=N8IK<37.85/77.77>,=N8KH<27.83/81.63>, + =N8NN<27.83/81.63>,=N8RW<27.83/81.63>,=N9EE<27.83/81.63>,=N9PGG<35.53/79.68>,=NA9J<27.83/81.63>, + =ND8L<27.83/81.63>,=NE8P<27.83/81.63>,=NF6P<27.83/81.63>,=NI7R<34.02/81.00>,=NJ8J<33.42/83.88>, + =NK7U<27.83/81.63>,=NL7WA<27.83/81.63>,=NN7CW<27.83/81.63>,=NN9DD<33.42/83.88>,=NO9E<33.42/83.88>, + =NP2D<27.83/81.63>,=NP2JV<27.83/81.63>,=NP3G<27.83/81.63>,=NP3K<27.83/81.63>,=NP3ST<27.83/81.63>, + =NP4CV<27.83/81.63>,=NT0K<35.53/79.68>,=NX9T<35.53/79.68>,=W0LL<27.83/81.63>,=W0NA<37.85/77.77>, + =W0PV<27.83/81.63>,=W5FB<37.85/77.77>,=W5TAF<37.85/77.77>,=W5VS<37.85/77.77>,=W6DVS<35.53/79.68>, + =W6HGF<37.85/77.77>,=W6KW<33.42/83.88>,=W6NWS<35.53/79.68>,=W7GKF<27.83/81.63>,=W7HJ<37.85/77.77>, + =W7IMP<27.83/81.63>,=W7IY<37.85/77.77>,=W8BLA<33.42/83.88>,=W8BRY<37.85/77.77>,=W8BT<37.85/77.77>, + =W8FN<35.53/79.68>,=W8IX<27.83/81.63>,=W8KRZ<37.85/77.77>,=W8WZ<35.53/79.68>,=W8XC<33.42/83.88>, + =W8ZN<37.85/77.77>,=W9BS<27.83/81.63>,=W9DC<34.02/81.00>,=W9GOL<27.83/81.63>,=W9MET<27.83/81.63>, + =W9TCV<27.83/81.63>,=WA0LJM<33.42/83.88>,=WA8AHZ<37.85/77.77>,=WA8OJR<34.02/81.00>, + =WA8YVF<35.53/79.68>,=WB0POH<37.85/77.77>,=WB5NHL<34.02/81.00>,=WB5RYB<33.42/83.88>, + =WB7PMP<35.53/79.68>,=WB8CZH<27.83/81.63>,=WB8PAF<27.83/81.63>,=WC0W<27.83/81.63>, + =WD0ETG<37.85/77.77>,=WD0FYV<37.85/77.77>,=WD9GKG<27.83/81.63>,=WE8USN<37.85/77.77>, + =WF7I<37.85/77.77>,=WF8E<33.42/83.88>,=WF9A<33.42/83.88>,=WH6A<27.83/81.63>,=WH6AQ<37.85/77.77>, + =WH6LE<35.53/79.68>,=WN7S<37.85/77.77>,=WO8L<35.53/79.68>,=WP2C<27.83/81.63>,=WP2J<27.83/81.63>, + =WP3ME<27.83/81.63>,=WP4BJS<34.02/81.00>,=WS6X<37.85/77.77>,=WS7X<27.83/81.63>, =WX7MB<34.02/81.00>, # AR-LA-MS-NM-OK-TX AA5(4)[7]<31.85/96.30>~6.0~,AB5(4)[7]<31.85/96.30>~6.0~,AC5(4)[7]<31.85/96.30>~6.0~, @@ -2905,40 +2909,44 @@ United States: 05: 08: NA: 37.60: 91.87: 5.0: K: WW5(4)[7]<31.85/96.30>~6.0~,WX5(4)[7]<31.85/96.30>~6.0~,WY5(4)[7]<31.85/96.30>~6.0~, WZ5(4)[7]<31.85/96.30>~6.0~,=AA3C(4)[7]<30.92/97.33>~6.0~,=AB5OR(4)[8]<32.58/89.57>~6.0~, =AB8YZ(4)[7]<30.92/97.33>~6.0~,=AC4CA(4)[7]<30.92/97.33>~6.0~,=AC7P(4)[7]<30.92/97.33>~6.0~, - =AH6SU(4)[8]<32.58/89.57>~6.0~,=AI5HG(4)[8]<32.58/89.57>~6.0~,=AJ4F(4)[7]<30.92/97.33>~6.0~, - =AK5CT(4)[8]<32.58/89.57>~6.0~,=AK9B(4)[7]<30.92/97.33>~6.0~,=AL5M(4)[7]<30.92/97.33>~6.0~, - =AL5P(4)[7]<30.92/97.33>~6.0~,=K1DW(4)[7]<30.92/97.33>~6.0~,=K1JD(4)[7]<34.60/106.33>~6.0~, + =AG5Z(4)[8]<32.58/89.57>~6.0~,=AH6SU(4)[8]<32.58/89.57>~6.0~,=AI5HG(4)[8]<32.58/89.57>~6.0~, + =AJ4F(4)[7]<30.92/97.33>~6.0~,=AK5CT(4)[8]<32.58/89.57>~6.0~,=AK9B(4)[7]<30.92/97.33>~6.0~, + =AL5M(4)[7]<30.92/97.33>~6.0~,=AL5P(4)[7]<30.92/97.33>~6.0~,=AL7V(4)[7]<35.60/96.85>~6.0~, + =K1DW(4)[7]<30.92/97.33>~6.0~,=K1IEB(4)[7]<30.92/97.33>~6.0~,=K1JD(4)[7]<34.60/106.33>~6.0~, =K3KEK(4)[7]<30.92/97.33>~6.0~,=K3NT(4)[7]<30.92/97.33>~6.0~,=K4DG(4)[7]<30.92/97.33>~6.0~, =K5OLV(4)[8]<32.58/89.57>~6.0~,=K5PN(4)[8]<32.58/89.57>~6.0~,=K5TRP(4)[8]<32.58/89.57>~6.0~, - =K5WK(4)[8]<32.58/89.57>~6.0~,=K5YVY(4)[8]<32.58/89.57>~6.0~,=K6ZB(4)[7]<30.92/97.33>~6.0~, - =K7DWI(4)[7]<30.92/97.33>~6.0~,=K7IA(4)[7]<34.60/106.33>~6.0~,=K7MOA(4)[7]<30.92/97.33>~6.0~, - =K7RB(4)[7]<30.92/97.33>~6.0~,=K7UD(4)[7]<30.92/97.33>~6.0~,=K7VF(4)[7]<30.92/97.33>~6.0~, - =K7ZYV(4)[8]<32.58/89.57>~6.0~,=K8LS(4)[7]<30.92/97.33>~6.0~,=K8OZ(4)[7]<34.60/106.33>~6.0~, - =K8TE(4)[7]<34.60/106.33>~6.0~,=K9OZ(4)[7]<35.20/92.72>~6.0~,=KA4OTB(4)[7]<35.20/92.72>~6.0~, - =KB1IKC(4)[7]<34.60/106.33>~6.0~,=KC2LM(4)[7]<34.60/106.33>~6.0~,=KC6UCN(4)[7]<30.92/97.33>~6.0~, - =KC6ZBE(4)[7]<30.92/97.33>~6.0~,=KC7QY(4)[7]<34.60/106.33>~6.0~,=KD2KW(4)[7]<30.92/97.33>~6.0~, - =KD5DD(4)[8]<32.58/89.57>~6.0~,=KE4XT(4)[7]<30.92/97.33>~6.0~,=KH9AE(4)[7]<30.92/97.33>~6.0~, - =KI7ID(4)[7]<30.92/97.33>~6.0~,=KJ6DQ(4)[7]<30.92/97.33>~6.0~,=KK6MC(4)[7]<34.60/106.33>~6.0~, - =KV8S(4)[7]<30.70/91.47>~6.0~,=N2GG(4)[7]<34.60/106.33>~6.0~,=N2IC(4)[7]<34.60/106.33>~6.0~, - =N3BB(4)[7]<30.92/97.33>~6.0~,=N3BUO(4)[7]<30.92/97.33>~6.0~,=N3CI(4)[7]<35.60/96.85>~6.0~, - =N3DI(4)[8]<30.70/91.47>~6.0~,=N4OGW(4)[8]<32.58/89.57>~6.0~,=N4UPX(4)[8]<32.58/89.57>~6.0~, + =K5UF(4)[8]<32.58/89.57>~6.0~,=K5WK(4)[8]<32.58/89.57>~6.0~,=K5YVY(4)[8]<32.58/89.57>~6.0~, + =K6FX(4)[7]<30.92/97.33>~6.0~,=K6ZB(4)[7]<30.92/97.33>~6.0~,=K7DWI(4)[7]<30.92/97.33>~6.0~, + =K7IA(4)[7]<34.60/106.33>~6.0~,=K7RB(4)[7]<30.92/97.33>~6.0~,=K7UD(4)[7]<30.92/97.33>~6.0~, + =K7VF(4)[7]<30.92/97.33>~6.0~,=K7ZYV(4)[8]<32.58/89.57>~6.0~,=K8LS(4)[7]<30.92/97.33>~6.0~, + =K8OZ(4)[7]<34.60/106.33>~6.0~,=K8TE(4)[7]<34.60/106.33>~6.0~,=K9OZ(4)[7]<35.20/92.72>~6.0~, + =KA4OTB(4)[7]<35.20/92.72>~6.0~,=KB1IKC(4)[7]<34.60/106.33>~6.0~,=KC2LM(4)[7]<34.60/106.33>~6.0~, + =KC6UCN(4)[7]<30.92/97.33>~6.0~,=KC6ZBE(4)[7]<30.92/97.33>~6.0~,=KC7QY(4)[7]<34.60/106.33>~6.0~, + =KD2KW(4)[7]<30.92/97.33>~6.0~,=KD5DD(4)[8]<32.58/89.57>~6.0~,=KE4XT(4)[7]<30.92/97.33>~6.0~, + =KE5MIS(4)[8]<32.58/89.57>~6.0~,=KF9LI(4)[7]<30.92/97.33>~6.0~,=KH9AE(4)[7]<30.92/97.33>~6.0~, + =KI5UTQ(4)[8]<32.58/89.57>~6.0~,=KI6HQT(4)[7]<30.92/97.33>~6.0~,=KI7ID(4)[7]<30.92/97.33>~6.0~, + =KJ6DQ(4)[7]<30.92/97.33>~6.0~,=KK6MC(4)[7]<34.60/106.33>~6.0~,=KV8S(4)[7]<30.70/91.47>~6.0~, + =N2GG(4)[7]<34.60/106.33>~6.0~,=N2IC(4)[7]<34.60/106.33>~6.0~,=N3BB(4)[7]<30.92/97.33>~6.0~, + =N3BUO(4)[7]<30.92/97.33>~6.0~,=N3CI(4)[7]<35.60/96.85>~6.0~,=N3DI(4)[8]<30.70/91.47>~6.0~, + =N3MRA(4)[7]<34.60/106.33>~6.0~,=N4OGW(4)[8]<32.58/89.57>~6.0~,=N4UPX(4)[8]<32.58/89.57>~6.0~, =N5CW(4)[8]<32.58/89.57>~6.0~,=N5YT(4)[8]<32.58/89.57>~6.0~,=N6ZZ(4)[7]<34.60/106.33>~6.0~, - =N8OO(4)[7]<30.70/91.47>~6.0~,=N9EXU(4)[7]<30.92/97.33>~6.0~,=N9NM(4)[7]<30.92/97.33>~6.0~, - =N9OF(4)[7]<30.92/97.33>~6.0~,=NA5NN(4)[8]<32.58/89.57>~6.0~,=NH6L(4)[7]<30.92/97.33>~6.0~, - =NH6PK(4)[8]<30.70/91.47>~6.0~,=NH7TR(4)[7]<30.92/97.33>~6.0~,=NI2M(4)[7]<30.92/97.33>~6.0~, - =NL7RQ(4)[7]<35.20/92.72>~6.0~,=NN5O(4)[8]<32.58/89.57>~6.0~,=W1JCW(4)[7]<30.92/97.33>~6.0~, + =N7KA(4)[7]<34.60/106.33>~6.0~,=N8OO(4)[7]<30.70/91.47>~6.0~,=N9EXU(4)[7]<30.92/97.33>~6.0~, + =N9NM(4)[7]<30.92/97.33>~6.0~,=N9OF(4)[7]<30.92/97.33>~6.0~,=NA5DX(4)[8]<32.58/89.57>~6.0~, + =NA5NN(4)[8]<32.58/89.57>~6.0~,=NH6L(4)[7]<30.92/97.33>~6.0~,=NH6PK(4)[8]<30.70/91.47>~6.0~, + =NH7TR(4)[7]<30.92/97.33>~6.0~,=NI2M(4)[7]<30.92/97.33>~6.0~,=NL7RQ(4)[7]<35.20/92.72>~6.0~, + =NN5O(4)[8]<32.58/89.57>~6.0~,=NZ7Q(4)[7]<34.60/106.33>~6.0~,=W1JCW(4)[7]<30.92/97.33>~6.0~, =W2ACY(4)[7]<30.92/97.33>~6.0~,=W2GS(4)[7]<32.58/89.57>~6.0~,=W2IY(4)[7]<34.60/106.33>~6.0~, =W2KF(4)[7]<30.92/97.33>~6.0~,=W2MN(4)[7]<30.92/97.33>~6.0~,=W2ODH(4)[7]<30.92/97.33>~6.0~, - =W3PWF(4)[7]<30.92/97.33>~6.0~,=W3RZ(4)[7]<30.92/97.33>~6.0~,=W5ORC(4)[8]<32.58/89.57>~6.0~, - =W5WGF(4)[8]<32.58/89.57>~6.0~,=W5YD(4)[8]<32.58/89.57>~6.0~,=W6FB(4)[7]<30.70/91.47>~6.0~, - =W6LAX(4)[7]<30.92/97.33>~6.0~,=W6LFB(4)[7]<30.92/97.33>~6.0~,=W8OV(4)[7]<30.92/97.33>~6.0~, - =W9DCT(4)[7]<35.20/92.72>~6.0~,=W9LCQ(4)[7]<30.92/97.33>~6.0~,=W9MC(4)[7]<30.92/97.33>~6.0~, - =WA2TOP(4)[7]<30.92/97.33>~6.0~,=WA2VYA(4)[7]<30.92/97.33>~6.0~,=WA8ZBT(4)[7]<30.92/97.33>~6.0~, - =WA9AFM(4)[7]<35.60/96.85>~6.0~,=WA9JBR(4)[7]<30.70/91.47>~6.0~,=WE6EZ(4)[7]<30.92/97.33>~6.0~, - =WF3H(4)[7]<30.92/97.33>~6.0~,=WH7R(4)[7]<30.92/97.33>~6.0~,=WL7OU(4)[7]<30.92/97.33>~6.0~, - =WM5A(4)[8]<32.58/89.57>~6.0~,=WM5DX(4)[8]<32.58/89.57>~6.0~,=WP4ADA(4)[7]<30.92/97.33>~6.0~, - =WQ5L(4)[8]<32.58/89.57>~6.0~,=WT4DX(4)[7]<30.92/97.33>~6.0~,=WX7V(4)[7]<30.92/97.33>~6.0~, - =WY6K(4)[7]<30.92/97.33>~6.0~, + =W3PWF(4)[7]<30.92/97.33>~6.0~,=W3RZ(4)[7]<30.92/97.33>~6.0~,=W3XO(4)[7]<30.92/97.33>~6.0~, + =W5ORC(4)[8]<32.58/89.57>~6.0~,=W5WGF(4)[8]<32.58/89.57>~6.0~,=W5YD(4)[8]<32.58/89.57>~6.0~, + =W6FB(4)[7]<30.70/91.47>~6.0~,=W6LAX(4)[7]<30.92/97.33>~6.0~,=W6LFB(4)[7]<30.92/97.33>~6.0~, + =W8OV(4)[7]<30.92/97.33>~6.0~,=W9DCT(4)[7]<35.20/92.72>~6.0~,=W9JEF(4)[7]<35.20/92.72>~6.0~, + =W9LCQ(4)[7]<30.92/97.33>~6.0~,=W9MC(4)[7]<30.92/97.33>~6.0~,=WA2TOP(4)[7]<30.92/97.33>~6.0~, + =WA2VYA(4)[7]<30.92/97.33>~6.0~,=WA8ZBT(4)[7]<30.92/97.33>~6.0~,=WA9AFM(4)[7]<35.60/96.85>~6.0~, + =WA9JBR(4)[7]<30.70/91.47>~6.0~,=WE6EZ(4)[7]<30.92/97.33>~6.0~,=WF3H(4)[7]<30.92/97.33>~6.0~, + =WH7R(4)[7]<30.92/97.33>~6.0~,=WL7OU(4)[7]<30.92/97.33>~6.0~,=WM5A(4)[8]<32.58/89.57>~6.0~, + =WM5DX(4)[8]<32.58/89.57>~6.0~,=WP4ADA(4)[7]<30.92/97.33>~6.0~,=WQ5L(4)[8]<32.58/89.57>~6.0~, + =WT4DX(4)[7]<30.92/97.33>~6.0~,=WX7V(4)[7]<30.92/97.33>~6.0~,=WY6K(4)[7]<30.92/97.33>~6.0~, # California AA6(3)[6]<35.48/119.35>~8.0~,AB6(3)[6]<35.48/119.35>~8.0~,AC6(3)[6]<35.48/119.35>~8.0~, AD6(3)[6]<35.48/119.35>~8.0~,AE6(3)[6]<35.48/119.35>~8.0~,AF6(3)[6]<35.48/119.35>~8.0~, @@ -2970,24 +2978,26 @@ United States: 05: 08: NA: 37.60: 91.87: 5.0: K: WZ6(3)[6]<35.48/119.35>~8.0~,=AA2IL(3)[6]<35.48/119.35>~8.0~,=AA5ND(3)[6]<35.48/119.35>~8.0~, =AB1U(3)[6]<35.48/119.35>~8.0~,=AG1RL(3)[6]<35.48/119.35>~8.0~,=AH0U(3)[6]<35.48/119.35>~8.0~, =K0AU(3)[6]<35.48/119.35>~8.0~,=K0DTJ(3)[6]<35.48/119.35>~8.0~,=K0NW(3)[6]<35.48/119.35>~8.0~, - =K2GMY(3)[6]<35.48/119.35>~8.0~,=K2RP(3)[6]<35.48/119.35>~8.0~,=K3EST(3)[6]<35.48/119.35>~8.0~, - =K5KMB(3)[6]<35.48/119.35>~8.0~,=K5OA(3)[6]<35.48/119.35>~8.0~,=K8TR(3)[6]<35.48/119.35>~8.0~, - =K9JM(3)[6]<35.48/119.35>~8.0~,=K9TAD(3)[6]<35.48/119.35>~8.0~,=K9YC(3)[6]<35.48/119.35>~8.0~, - =KA5WSS(3)[6]<35.48/119.35>~8.0~,=KA9A(3)[6]<35.48/119.35>~8.0~,=KB9ENS(3)[6]<35.48/119.35>~8.0~, - =KC8J(3)[6]<35.48/119.35>~8.0~,=KE1B(3)[6]<35.48/119.35>~8.0~,=KE8FT(3)[6]<35.48/119.35>~8.0~, + =K0XP(3)[6]<35.48/119.35>~8.0~,=K2GMY(3)[6]<35.48/119.35>~8.0~,=K2RP(3)[6]<35.48/119.35>~8.0~, + =K3EST(3)[6]<35.48/119.35>~8.0~,=K3GX(3)[6]<35.48/119.35>~8.0~,=K5KMB(3)[6]<35.48/119.35>~8.0~, + =K5OA(3)[6]<35.48/119.35>~8.0~,=K8TR(3)[6]<35.48/119.35>~8.0~,=K9JM(3)[6]<35.48/119.35>~8.0~, + =K9TAD(3)[6]<35.48/119.35>~8.0~,=K9YC(3)[6]<35.48/119.35>~8.0~,=KA5WSS(3)[6]<35.48/119.35>~8.0~, + =KA9A(3)[6]<35.48/119.35>~8.0~,=KB9ENS(3)[6]<35.48/119.35>~8.0~,=KC8J(3)[6]<35.48/119.35>~8.0~, + =KE1B(3)[6]<35.48/119.35>~8.0~,=KE5AX(3)[6]<35.48/119.35>~8.0~,=KE8FT(3)[6]<35.48/119.35>~8.0~, =KE8NN(3)[6]<35.48/119.35>~8.0~,=KH2BD(3)[6]<35.48/119.35>~8.0~,=KH2BR(3)[6]<35.48/119.35>~8.0~, =KH2SR(3)[6]<35.48/119.35>~8.0~,=KH2TJ(3)[6]<35.48/119.35>~8.0~,=KH6GK(3)[6]<35.48/119.35>~8.0~, =KH6JRC(3)[6]<35.48/119.35>~8.0~,=KL7AN(3)[6]<35.48/119.35>~8.0~,=KL7DJ(3)[6]<35.48/119.35>~8.0~, - =KL7HQR(3)[6]<35.48/119.35>~8.0~,=KY0W(3)[6]<35.48/119.35>~8.0~,=N2JNR(3)[6]<35.48/119.35>~8.0~, - =N3KA(3)[6]<35.48/119.35>~8.0~,=N3RC(3)[6]<35.48/119.35>~8.0~,=N3ZZ(3)[6]<35.48/119.35>~8.0~, - =N4DLA(3)[6]<35.48/119.35>~8.0~,=N5KO(3)[6]<35.48/119.35>~8.0~,=N5ZO(3)[6]<35.48/119.35>~8.0~, - =N9BD(3)[6]<35.48/119.35>~8.0~,=ND2T(3)[6]<35.48/119.35>~8.0~,=NH6G(3)[6]<35.48/119.35>~8.0~, - =NK3L(3)[6]<35.48/119.35>~8.0~,=NR1K(3)[6]<35.48/119.35>~8.0~,=NT5A(3)[6]<35.48/119.35>~8.0~, - =W0YK(3)[6]<35.48/119.35>~8.0~,=W1MVY(3)[6]<35.48/119.35>~8.0~,=W1PR(3)[6]<35.48/119.35>~8.0~, - =W1RH(3)[6]<35.48/119.35>~8.0~,=W1SAV(3)[6]<35.48/119.35>~8.0~,=W1SRD(3)[6]<35.48/119.35>~8.0~, - =W3FF(3)[6]<35.48/119.35>~8.0~,=W4EF(3)[6]<35.48/119.35>~8.0~,=W4UAT(3)[6]<35.48/119.35>~8.0~, - =W8GJK(3)[6]<35.48/119.35>~8.0~,=W9KKN(3)[6]<35.48/119.35>~8.0~,=W9MAK(3)[6]<35.48/119.35>~8.0~, - =WB8YQJ(3)[6]<35.48/119.35>~8.0~,=WC2A(3)[6]<35.48/119.35>~8.0~,=WX5S(3)[6]<35.48/119.35>~8.0~, + =KL7HQR(3)[6]<35.48/119.35>~8.0~,=KY0W(3)[6]<35.48/119.35>~8.0~,=N3KA(3)[6]<35.48/119.35>~8.0~, + =N3RC(3)[6]<35.48/119.35>~8.0~,=N3ZZ(3)[6]<35.48/119.35>~8.0~,=N4DLA(3)[6]<35.48/119.35>~8.0~, + =N5KO(3)[6]<35.48/119.35>~8.0~,=N5ZO(3)[6]<35.48/119.35>~8.0~,=N9BD(3)[6]<35.48/119.35>~8.0~, + =ND2T(3)[6]<35.48/119.35>~8.0~,=NH6G(3)[6]<35.48/119.35>~8.0~,=NK3L(3)[6]<35.48/119.35>~8.0~, + =NR1K(3)[6]<35.48/119.35>~8.0~,=NR5NN(3)[6]<35.48/119.35>~8.0~,=NT5A(3)[6]<35.48/119.35>~8.0~, + =W0PE(3)[6]<35.48/119.35>~8.0~,=W0YK(3)[6]<35.48/119.35>~8.0~,=W1MVY(3)[6]<35.48/119.35>~8.0~, + =W1PR(3)[6]<35.48/119.35>~8.0~,=W1RH(3)[6]<35.48/119.35>~8.0~,=W1SAV(3)[6]<35.48/119.35>~8.0~, + =W1SRD(3)[6]<35.48/119.35>~8.0~,=W3FF(3)[6]<35.48/119.35>~8.0~,=W4EF(3)[6]<35.48/119.35>~8.0~, + =W4UAT(3)[6]<35.48/119.35>~8.0~,=W8GJK(3)[6]<35.48/119.35>~8.0~,=W9KKN(3)[6]<35.48/119.35>~8.0~, + =W9MAK(3)[6]<35.48/119.35>~8.0~,=WB8YQJ(3)[6]<35.48/119.35>~8.0~,=WC2A(3)[6]<35.48/119.35>~8.0~, + =WX5S(3)[6]<35.48/119.35>~8.0~, # AZ-ID-MT-NV-OR-UT-WA-WY AA7(3)[6]<41.30/116.60>~7.0~,AB7(3)[6]<41.30/116.60>~7.0~,AC7(3)[6]<41.30/116.60>~7.0~, AD7(3)[6]<41.30/116.60>~7.0~,AE7(3)[6]<41.30/116.60>~7.0~,AF7(3)[6]<41.30/116.60>~7.0~, @@ -3025,26 +3035,30 @@ United States: 05: 08: NA: 37.60: 91.87: 5.0: K: =K3RN(3)[6]<33.37/111.88>~7.0~,=K4XU(3)[6]<44.75/122.58>~7.0~,=K4YO(3)[6]<40.38/111.95>~7.0~, =K5HDU(3)[6]<40.38/111.95>~7.0~,=K5ML(3)[6]<33.37/111.88>~7.0~,=K8IA(3)[6]<33.37/111.88>~7.0~, =K9JF(3)[6]<47.33/121.63>~7.0~,=K9PY(3)[6]<44.75/122.58>~7.0~,=K9RZ(3)[6]<33.37/111.88>~7.0~, - =K9VIT(3)[6]<40.38/111.95>~7.0~,=K9WZB(3)[6]<33.37/111.88>~7.0~,=KB2S(3)[6]<47.33/121.63>~7.0~, + =K9VIT(3)[6]<40.38/111.95>~7.0~,=K9WZB(3)[6]<33.37/111.88>~7.0~,=KC1BB(3)[6]<33.37/111.88>~7.0~, =KD2FSH(3)[6]<33.37/111.88>~7.0~,=KD5RBU(3)[6]<47.33/121.63>~7.0~,=KE2VB(3)[6]<33.37/111.88>~7.0~, - =KH6ND(3)[6]<41.30/116.60>~7.0~,=KH6NO(3)[6]<37.02/116.17>~7.0~,=KH6VM(3)[6]<47.33/121.63>~7.0~, - =KL1JF(3)[6]<44.75/122.58>~7.0~,=KL5CS(3)[6]<44.75/122.58>~7.0~,=KL7IKV(3)[6]<47.33/121.63>~7.0~, - =KN0W(3)[6]<47.33/121.63>~7.0~,=KU1CW(3)[6]<47.33/121.63>~7.0~,=KZ1W(3)[6]<47.33/121.63>~7.0~, - =N0KRE(3)[6]<33.37/111.88>~7.0~,=N1AL(3)[6]<40.38/111.95>~7.0~,=N1JM(3)[6]<33.37/111.88>~7.0~, - =N3AIU(3)[6]<33.37/111.88>~7.0~,=N3EG(3)[6]<47.33/121.63>~7.0~,=N4CC(3)[6]<44.22/115.22>~7.0~, - =N5CR(3)[6]<47.33/121.63>~7.0~,=N8ELA(3)[6]<33.37/111.88>~7.0~,=N8WCR(3)[6]<47.33/121.63>~7.0~, - =N9NA(3)[6]<33.37/111.88>~7.0~,=NA2U(3)[6]<33.37/111.88>~7.0~,=NH6Z(3)[6]<44.75/122.58>~7.0~, - =NL7D(3)[6]<33.37/111.88>~7.0~,=NL7GN(3)[6]<44.75/122.58>~7.0~,=NL7GW(3)[6]<44.75/122.58>~7.0~, - =NX1P(3)[6]<44.75/122.58>~7.0~,=NZ2S(3)[6]<47.33/121.63>~7.0~,=W0BF(3)[6]<33.37/111.88>~7.0~, - =W0NF(3)[6]<33.37/111.88>~7.0~,=W0RIC(3)[6]<33.37/111.88>~7.0~,=W0VQ(3)[6]<47.33/121.63>~7.0~, - =W0ZZ(3)[6]<33.37/111.88>~7.0~,=W1DGL(3)[6]<33.37/111.88>~7.0~,=W1YY(3)[6]<47.33/121.63>~7.0~, - =W2AA(3)[6]<37.02/116.17>~7.0~,=W2FV(3)[6]<47.33/121.63>~7.0~,=W2HZ(3)[6]<33.37/111.88>~7.0~, - =W2NQ(3)[6]<44.75/122.58>~7.0~,=W2VJN(3)[6]<44.75/122.58>~7.0~,=W4IDX(3)[6]<33.37/111.88>~7.0~, - =W4IX(3)[6]<33.37/111.88>~7.0~,=W5OBT(3)[6]<33.37/111.88>~7.0~,=W5OT(3)[6]<33.37/111.88>~7.0~, - =W8NOR(3)[6]<33.37/111.88>~7.0~,=W8TK(3)[6]<33.37/111.88>~7.0~,=W9CF(3)[6]<33.37/111.88>~7.0~, - =W9PL(3)[6]<47.33/121.63>~7.0~,=WA0PFC(3)[6]<40.38/111.95>~7.0~,=WA0WWW(3)[6]<47.33/121.63>~7.0~, - =WA8ZNC(3)[6]<47.33/121.63>~7.0~,=WB2AWQ(3)[6]<37.02/116.17>~7.0~, - =WB3JFS(3)[6]<37.02/116.17>~7.0~,=WB7UOF(3)[7]<33.37/111.88>~7.0~, + =KE4EW(3)[6]<44.22/115.22>~7.0~,=KH6ND(3)[6]<41.30/116.60>~7.0~,=KH6NO(3)[6]<37.02/116.17>~7.0~, + =KH6VM(3)[6]<47.33/121.63>~7.0~,=KL1JF(3)[6]<44.75/122.58>~7.0~,=KL5CS(3)[6]<44.75/122.58>~7.0~, + =KL7FDQ(3)[6]<47.33/121.63>~7.0~,=KL7IKV(3)[6]<47.33/121.63>~7.0~,=KL7OF(3)[6]<47.33/121.63>~7.0~, + =KL7SK(3)[6]<47.33/121.63>~7.0~,=KN0W(3)[6]<47.33/121.63>~7.0~,=KU1CW(3)[6]<47.33/121.63>~7.0~, + =KZ1W(3)[6]<47.33/121.63>~7.0~,=N0KRE(3)[6]<33.37/111.88>~7.0~,=N1AL(3)[6]<40.38/111.95>~7.0~, + =N1JM(3)[6]<33.37/111.88>~7.0~,=N3AIU(3)[6]<33.37/111.88>~7.0~,=N3EG(3)[6]<47.33/121.63>~7.0~, + =N4CC(3)[6]<44.22/115.22>~7.0~,=N5CR(3)[6]<47.33/121.63>~7.0~,=N8ELA(3)[6]<33.37/111.88>~7.0~, + =N8WCR(3)[6]<47.33/121.63>~7.0~,=N9NA(3)[6]<33.37/111.88>~7.0~,=NA2U(3)[6]<33.37/111.88>~7.0~, + =NE7DX(3)[7]<33.37/111.88>~7.0~,=NH6Z(3)[6]<44.75/122.58>~7.0~,=NL7D(3)[6]<33.37/111.88>~7.0~, + =NL7GN(3)[6]<44.75/122.58>~7.0~,=NL7GW(3)[6]<44.75/122.58>~7.0~,=NX1P(3)[6]<44.75/122.58>~7.0~, + =NZ2S(3)[6]<47.33/121.63>~7.0~,=W0BF(3)[6]<33.37/111.88>~7.0~,=W0NF(3)[6]<33.37/111.88>~7.0~, + =W0VQ(3)[6]<47.33/121.63>~7.0~,=W0ZZ(3)[6]<33.37/111.88>~7.0~,=W1DGL(3)[6]<33.37/111.88>~7.0~, + =W1ESE(3)[6]<33.37/111.88>~7.0~,=W1YY(3)[6]<47.33/121.63>~7.0~,=W2AA(3)[6]<37.02/116.17>~7.0~, + =W2FV(3)[6]<47.33/121.63>~7.0~,=W2HZ(3)[6]<33.37/111.88>~7.0~,=W2NQ(3)[6]<44.75/122.58>~7.0~, + =W2VJN(3)[6]<44.75/122.58>~7.0~,=W3NX(3)[6]<33.37/111.88>~7.0~,=W4IDX(3)[6]<33.37/111.88>~7.0~, + =W4IX(3)[6]<33.37/111.88>~7.0~,=W4LOO(3)[6]<40.38/111.95>~7.0~,=W5OBT(3)[6]<33.37/111.88>~7.0~, + =W5OT(3)[6]<33.37/111.88>~7.0~,=W7DMW(3)[7]<40.38/111.95>~7.0~,=W8NOR(3)[6]<33.37/111.88>~7.0~, + =W8TK(3)[6]<33.37/111.88>~7.0~,=W9CF(3)[6]<33.37/111.88>~7.0~,=W9PL(3)[6]<47.33/121.63>~7.0~, + =WA0PFC(3)[6]<40.38/111.95>~7.0~,=WA0WWW(3)[6]<47.33/121.63>~7.0~, + =WA2EDJ(3)[6]<33.37/111.88>~7.0~,=WA8ZNC(3)[6]<47.33/121.63>~7.0~, + =WB2AWQ(3)[6]<37.02/116.17>~7.0~,=WB3JFS(3)[6]<37.02/116.17>~7.0~, + =WB5NOE(3)[6]<40.38/111.95>~7.0~,=WB7UOF(3)[7]<33.37/111.88>~7.0~, =WH6FZY(3)[6]<47.33/121.63>~7.0~,=WJ9B(3)[6]<44.22/115.22>~7.0~,=WN9J(3)[6]<47.33/121.63>~7.0~, =WR5J(3)[6]<47.33/121.63>~7.0~,=WT8P(3)[6]<47.33/121.63>~7.0~,=WU9B(3)[6]<33.37/111.88>~7.0~, =WZ8T(3)[6]<47.33/121.63>~7.0~, @@ -3069,22 +3083,21 @@ United States: 05: 08: NA: 37.60: 91.87: 5.0: K: WK8(4)<41.35/83.23>,WM8(4)<41.35/83.23>,WN8(4)<41.35/83.23>,WO8(4)<41.35/83.23>, WQ8(4)<41.35/83.23>,WR8(4)<41.35/83.23>,WS8(4)<41.35/83.23>,WT8(4)<41.35/83.23>, WU8(4)<41.35/83.23>,WV8(4)<41.35/83.23>,WW8(4)<41.35/83.23>,WX8(4)<41.35/83.23>, - WY8(4)<41.35/83.23>,WZ8(4)<41.35/83.23>,=AA1JM(4)<42.87/84.22>,=AA4R(4)<42.87/84.22>, - =AH2AV(4)<42.87/84.22>,=K0ACP(4)<42.87/84.22>,=K0TEA(4)<42.87/84.22>,=K1CCN(4)<40.43/82.80>, - =K1LT(4)<40.43/82.80>,=K2CUB(4)<42.87/84.22>,=K2YAZ(4)<42.87/84.22>,=K3DMG(4)<40.43/82.80>, - =K3GP(4)<40.43/82.80>,=K3USC(4)<40.43/82.80>,=K3VIN(4)<40.43/82.80>,=K3YP(4)<40.43/82.80>, - =K4YJ(4)<40.43/82.80>,=K7DR(4)<42.87/84.22>,=KA0AZS(4)<40.43/82.80>,=KA1RUE(4)<40.43/82.80>, - =KB4TLH(4)<40.43/82.80>,=KB6NU(4)<42.87/84.22>,=KE3K(4)<42.87/84.22>,=KI6DY(4)<40.43/82.80>, - =KK0ECT(4)<42.87/84.22>,=KL2YE(4)<42.87/84.22>,=KP4VZ(4)<40.43/82.80>,=N0FW(4)<40.43/82.80>, - =N3HE(4)<40.43/82.80>,=N3RA(4)<40.43/82.80>,=N4EL(4)<40.43/82.80>,=N4HAI(4)<40.43/82.80>, - =N4LSJ(4)<40.43/82.80>,=N5JED(4)<40.43/82.80>,=N5MKY(4)<42.87/84.22>,=N5WCS(4)<42.87/84.22>, - =N6FS(4)<42.87/84.22>,=N6JRL(4)<40.43/82.80>,=N6VF(4)<40.43/82.80>,=ND3N(4)<40.43/82.80>, - =W0CG(4)<40.43/82.80>,=W0JX(4)<40.43/82.80>,=W1NN(4)<40.43/82.80>,=W1PD(4)<40.43/82.80>, - =W1SKU(4)<42.87/84.22>,=W3CB(4)<42.87/84.22>,=W3CRZ(4)<40.43/82.80>,=W3HKK(4)<40.43/82.80>, - =W3USA(4)<40.43/82.80>,=W4KD(4)<40.43/82.80>,=W4YPW(4)<40.43/82.80>,=W5DT(4)<42.87/84.22>, - =W5KBW(4)<40.43/82.80>,=W5UHQ(4)<40.43/82.80>,=WA1UJU(4)<42.87/84.22>,=WA2DHG(4)<40.43/82.80>, - =WA3C(4)<40.43/82.80>,=WA3JAT(4)<40.43/82.80>,=WO3X(4)<40.43/82.80>,=WR4T(4)<40.43/82.80>, - =WS6K(4)<40.43/82.80>, + WY8(4)<41.35/83.23>,WZ8(4)<41.35/83.23>,=AA4R(4)<42.87/84.22>,=AA5TA(4)<40.43/82.80>, + =AH2AV(4)<42.87/84.22>,=K1CCN(4)<40.43/82.80>,=K1LT(4)<40.43/82.80>,=K2CUB(4)<42.87/84.22>, + =K2YAZ(4)<42.87/84.22>,=K3DMG(4)<40.43/82.80>,=K3GP(4)<40.43/82.80>,=K3USC(4)<40.43/82.80>, + =K3VIN(4)<40.43/82.80>,=K3YP(4)<40.43/82.80>,=K4YJ(4)<40.43/82.80>,=K7DR(4)<42.87/84.22>, + =KA0AZS(4)<40.43/82.80>,=KA1RUE(4)<40.43/82.80>,=KB4TLH(4)<40.43/82.80>,=KB6NU(4)<42.87/84.22>, + =KE3K(4)<42.87/84.22>,=KI6DY(4)<40.43/82.80>,=KL2YE(4)<42.87/84.22>,=KP4VZ(4)<40.43/82.80>, + =N0FW(4)<40.43/82.80>,=N3HE(4)<40.43/82.80>,=N3RA(4)<40.43/82.80>,=N4EL(4)<40.43/82.80>, + =N4HAI(4)<40.43/82.80>,=N4LSJ(4)<40.43/82.80>,=N5JED(4)<40.43/82.80>,=N5MKY(4)<42.87/84.22>, + =N5WCS(4)<42.87/84.22>,=N6FS(4)<42.87/84.22>,=N6JRL(4)<40.43/82.80>,=N6VF(4)<40.43/82.80>, + =ND3N(4)<40.43/82.80>,=W0CG(4)<40.43/82.80>,=W0JX(4)<40.43/82.80>,=W1CHL(4)<40.43/82.80>, + =W1NN(4)<40.43/82.80>,=W1PD(4)<40.43/82.80>,=W1SKU(4)<42.87/84.22>,=W3CB(4)<42.87/84.22>, + =W3CRZ(4)<40.43/82.80>,=W3HKK(4)<40.43/82.80>,=W3USA(4)<40.43/82.80>,=W4KD(4)<40.43/82.80>, + =W4YPW(4)<40.43/82.80>,=W5DT(4)<42.87/84.22>,=W5HIO(4)<40.43/82.80>,=W5KBW(4)<40.43/82.80>, + =W5UHQ(4)<40.43/82.80>,=WA1UJU(4)<42.87/84.22>,=WA2DHG(4)<40.43/82.80>,=WA3C(4)<40.43/82.80>, + =WA3JAT(4)<40.43/82.80>,=WO3X(4)<40.43/82.80>,=WR4T(4)<40.43/82.80>,=WS6K(4)<40.43/82.80>, # IL-IN-WI AA9(4)<41.55/87.97>~6.0~,AB9(4)<41.55/87.97>~6.0~,AC9(4)<41.55/87.97>~6.0~, AD9(4)<41.55/87.97>~6.0~,AE9(4)<41.55/87.97>~6.0~,AF9(4)<41.55/87.97>~6.0~, @@ -3113,30 +3126,33 @@ United States: 05: 08: NA: 37.60: 91.87: 5.0: K: WQ9(4)<41.55/87.97>~6.0~,WR9(4)<41.55/87.97>~6.0~,WS9(4)<41.55/87.97>~6.0~, WT9(4)<41.55/87.97>~6.0~,WU9(4)<41.55/87.97>~6.0~,WV9(4)<41.55/87.97>~6.0~, WW9(4)<41.55/87.97>~6.0~,WX9(4)<41.55/87.97>~6.0~,WY9(4)<41.55/87.97>~6.0~, - WZ9(4)<41.55/87.97>~6.0~,=AC9TO(4)[7]<43.72/89.03>~6.0~,=AI9K(4)[7]<41.32/88.37>~6.0~, - =K0PG(4)<41.32/88.37>~6.0~,=K0PJ(4)<43.72/89.03>~6.0~,=K0TQ(4)<40.15/86.25>~6.0~, - =K2DRH(4)[7]<41.32/88.37>~6.0~,=K7CS(4)<41.32/88.37>~6.0~,=K9ILS(4)[7]<41.32/88.37>~6.0~, - =K9MU(4)[7]<43.72/89.03>~6.0~,=K9QC(4)[7]<43.72/89.03>~6.0~,=K9VD(4)[7]<41.32/88.37>~6.0~, - =K9WA(4)[7]<41.32/88.37>~6.0~,=KA2MLH(4)<41.32/88.37>~6.0~,=KA9FOX(4)[7]<43.72/89.03>~6.0~, - =KA9VVQ(4)[7]<43.72/89.03>~6.0~,=KB9S(4)[7]<43.72/89.03>~6.0~,=KC9K(4)[7]<41.32/88.37>~6.0~, + WZ9(4)<41.55/87.97>~6.0~,=AC9TO(4)[7]<43.72/89.03>~6.0~,=AG9G(4)[7]<43.72/89.03>~6.0~, + =AI9K(4)[7]<41.32/88.37>~6.0~,=K0PG(4)<41.32/88.37>~6.0~,=K0PJ(4)<43.72/89.03>~6.0~, + =K0TQ(4)<40.15/86.25>~6.0~,=K2DRH(4)[7]<41.32/88.37>~6.0~,=K4NAX(4)<40.15/86.25>~6.0~, + =K7CS(4)<41.32/88.37>~6.0~,=K9ILS(4)[7]<41.32/88.37>~6.0~,=K9MU(4)[7]<43.72/89.03>~6.0~, + =K9QC(4)[7]<43.72/89.03>~6.0~,=K9VD(4)[7]<41.32/88.37>~6.0~,=K9WA(4)[7]<41.32/88.37>~6.0~, + =KA2MLH(4)<41.32/88.37>~6.0~,=KA9FOX(4)[7]<43.72/89.03>~6.0~,=KA9VVQ(4)[7]<43.72/89.03>~6.0~, + =KB9RUG(4)[7]<41.32/88.37>~6.0~,=KB9S(4)[7]<43.72/89.03>~6.0~,=KC9K(4)[7]<41.32/88.37>~6.0~, =KD0EE(4)<40.15/86.25>~6.0~,=KD4ULW(4)<40.15/86.25>~6.0~,=KD9LTN(4)[7]<41.32/88.37>~6.0~, =KE0YI(4)<41.32/88.37>~6.0~,=KO4YIN(4)<43.72/89.03>~6.0~,=KP2XX(4)[7]<43.72/89.03>~6.0~, - =KV3T(4)<41.32/88.37>~6.0~,=KW9A(4)[7]<41.32/88.37>~6.0~,=KY0Q(4)<41.32/88.37>~6.0~, - =KY6AA(4)<41.32/88.37>~6.0~,=KZ9V(4)[7]<43.72/89.03>~6.0~,=N1KW(4)<41.32/88.37>~6.0~, - =N2BJ(4)<41.32/88.37>~6.0~,=N4BAA(4)<40.15/86.25>~6.0~,=N4SV(4)<40.15/86.25>~6.0~, - =N4TZ(4)<40.15/86.25>~6.0~,=N5EP(4)<41.32/88.37>~6.0~,=N5RP(4)<41.32/88.37>~6.0~, - =N7US(4)<41.32/88.37>~6.0~,=N7ZZ(4)<43.72/89.03>~6.0~,=N9ATF(4)[7]<41.32/88.37>~6.0~, - =N9CHA(4)[7]<43.72/89.03>~6.0~,=N9CIQ(4)[7]<43.72/89.03>~6.0~,=N9EA(4)[7]<41.32/88.37>~6.0~, - =N9JF(4)[7]<41.32/88.37>~6.0~,=N9SB(4)[7]<41.32/88.37>~6.0~,=N9VPV(4)[7]<41.32/88.37>~6.0~, - =NA9RB(4)[7]<41.32/88.37>~6.0~,=NE9U(4)[7]<43.72/89.03>~6.0~,=NL7QC(4)[7]<43.72/89.03>~6.0~, - =NL7XT(4)<40.15/86.25>~6.0~,=NP2WJ(4)<41.32/88.37>~6.0~,=NQ6N(4)<41.32/88.37>~6.0~, - =NY1V(4)<40.15/86.25>~6.0~,=W1CLM(4)<41.32/88.37>~6.0~,=W3HDH(4)<41.32/88.37>~6.0~, - =W7FW(4)<41.32/88.37>~6.0~,=W9AVM(4)[7]<43.72/89.03>~6.0~,=W9AWE(4)[7]<41.32/88.37>~6.0~, - =W9DKB(4)[7]<43.72/89.03>~6.0~,=W9ET(4)[7]<43.72/89.03>~6.0~,=W9FZ(4)[7]<43.72/89.03>~6.0~, - =W9JA(4)[7]<43.72/89.03>~6.0~,=W9MVA(4)[7]<43.72/89.03>~6.0~,=W9RNY(4)[7]<41.32/88.37>~6.0~, - =W9WE(4)[7]<41.32/88.37>~6.0~,=W9XG(4)[7]<41.32/88.37>~6.0~,=W9YZR(4)[7]<43.72/89.03>~6.0~, - =WA2USA(4)<40.15/86.25>~6.0~,=WA9TZW(4)[7]<41.32/88.37>~6.0~,=WH0AI(4)[7]<43.72/89.03>~6.0~, - =WQ5O(4)<43.72/89.03>~6.0~,=WQ9T(4)[7]<41.32/88.37>~6.0~,=WT2P(4)<41.32/88.37>~6.0~, + =KQ9J(4)[7]<43.72/89.03>~6.0~,=KV3T(4)<41.32/88.37>~6.0~,=KW9A(4)[7]<41.32/88.37>~6.0~, + =KY0Q(4)<41.32/88.37>~6.0~,=KY6AA(4)<41.32/88.37>~6.0~,=KZ9V(4)[7]<43.72/89.03>~6.0~, + =N1KW(4)<41.32/88.37>~6.0~,=N2BJ(4)<41.32/88.37>~6.0~,=N4BAA(4)<40.15/86.25>~6.0~, + =N4SV(4)<40.15/86.25>~6.0~,=N4TZ(4)<40.15/86.25>~6.0~,=N5EP(4)<41.32/88.37>~6.0~, + =N5RP(4)<41.32/88.37>~6.0~,=N7US(4)<41.32/88.37>~6.0~,=N7ZZ(4)<43.72/89.03>~6.0~, + =N9ATF(4)[7]<41.32/88.37>~6.0~,=N9CHA(4)[7]<43.72/89.03>~6.0~,=N9CIQ(4)[7]<43.72/89.03>~6.0~, + =N9EA(4)[7]<41.32/88.37>~6.0~,=N9JF(4)[7]<41.32/88.37>~6.0~,=N9SB(4)[7]<41.32/88.37>~6.0~, + =N9VPV(4)[7]<41.32/88.37>~6.0~,=NA9RB(4)[7]<41.32/88.37>~6.0~,=NE9U(4)[7]<43.72/89.03>~6.0~, + =NL7QC(4)[7]<43.72/89.03>~6.0~,=NL7XT(4)<40.15/86.25>~6.0~,=NP2WJ(4)<41.32/88.37>~6.0~, + =NQ6N(4)<41.32/88.37>~6.0~,=NV9J(4)[7]<43.72/89.03>~6.0~,=NY1V(4)<40.15/86.25>~6.0~, + =W1CLM(4)<41.32/88.37>~6.0~,=W1QC(4)<43.72/89.03>~6.0~,=W3HDH(4)<41.32/88.37>~6.0~, + =W3LZ(4)<43.72/89.03>~6.0~,=W7FW(4)<41.32/88.37>~6.0~,=W9AVM(4)[7]<43.72/89.03>~6.0~, + =W9AWE(4)[7]<41.32/88.37>~6.0~,=W9DKB(4)[7]<43.72/89.03>~6.0~,=W9ET(4)[7]<43.72/89.03>~6.0~, + =W9FZ(4)[7]<43.72/89.03>~6.0~,=W9JA(4)[7]<43.72/89.03>~6.0~,=W9MVA(4)[7]<43.72/89.03>~6.0~, + =W9RNY(4)[7]<41.32/88.37>~6.0~,=W9WE(4)[7]<41.32/88.37>~6.0~,=W9XG(4)[7]<41.32/88.37>~6.0~, + =W9YZR(4)[7]<43.72/89.03>~6.0~,=WA2USA(4)<40.15/86.25>~6.0~,=WA9TZW(4)[7]<41.32/88.37>~6.0~, + =WH0AI(4)[7]<43.72/89.03>~6.0~,=WQ5O(4)<43.72/89.03>~6.0~,=WQ9T(4)[7]<41.32/88.37>~6.0~, + =WT2P(4)<41.32/88.37>~6.0~, # AL-KY-TN =AA2MA(4)<33.02/86.75>~6.0~,=AA4DD(4)<35.82/86.33>~6.0~,=AA4MB(4)<35.82/86.33>~6.0~, =AA4NU(4)<35.82/86.33>~6.0~,=AA4U(4)<33.02/86.75>~6.0~,=AA4WW(4)<37.83/85.27>~6.0~, @@ -3144,89 +3160,95 @@ United States: 05: 08: NA: 37.60: 91.87: 5.0: K: =AB4IQ(4)<37.83/85.27>~6.0~,=AC4G(4)<35.82/86.33>~6.0~,=AC6NT(4)<35.82/86.33>~6.0~, =AC6ZM(4)<35.82/86.33>~6.0~,=AD4EB(4)<35.82/86.33>~6.0~,=AD4TA(4)<37.83/85.27>~6.0~, =AE4ED(4)<33.02/86.75>~6.0~,=AF1US(4)<35.82/86.33>~6.0~,=AF4T(4)<35.82/86.33>~6.0~, - =AG4W(4)<33.02/86.75>~6.0~,=AH6FF(4)<33.02/86.75>~6.0~,=AI4DB(4)<35.82/86.33>~6.0~, - =AJ4A(4)<37.83/85.27>~6.0~,=AJ4SB(4)<35.82/86.33>~6.0~,=AJ6T(4)<35.82/86.33>~6.0~, - =AK4I(4)<35.82/86.33>~6.0~,=AK4Z(4)<35.82/86.33>~6.0~,=AL4B(4)<35.82/86.33>~6.0~, - =AL4US(4)<35.82/86.33>~6.0~,=K0EJ(4)<35.82/86.33>~6.0~,=K0RGI(4)<35.82/86.33>~6.0~, - =K1GU(4)<35.82/86.33>~6.0~,=K1MWH(4)<35.82/86.33>~6.0~,=K1VX(4)<35.82/86.33>~6.0~, - =K3IE(4)<35.82/86.33>~6.0~,=K3JWI(4)<35.82/86.33>~6.0~,=K4AB(4)<33.02/86.75>~6.0~, - =K4AFE(4)<35.82/86.33>~6.0~,=K4AL(4)<35.82/86.33>~6.0~,=K4AMC(4)<35.82/86.33>~6.0~, - =K4AVX(4)<37.83/85.27>~6.0~,=K4BFT(4)<33.02/86.75>~6.0~,=K4BWP(4)<37.83/85.27>~6.0~, - =K4BX(4)<35.82/86.33>~6.0~,=K4CNY(4)<33.02/86.75>~6.0~,=K4DR(4)<35.82/86.33>~6.0~, - =K4DXV(4)<35.82/86.33>~6.0~,=K4DZR(4)<35.82/86.33>~6.0~,=K4EES(4)<33.02/86.75>~6.0~, - =K4FN(4)<37.83/85.27>~6.0~,=K4HAL(4)<33.02/86.75>~6.0~,=K4IE(4)<37.83/85.27>~6.0~, + =AG4W(4)<33.02/86.75>~6.0~,=AG5AT(4)<33.02/86.75>~6.0~,=AH6FF(4)<33.02/86.75>~6.0~, + =AI4DB(4)<35.82/86.33>~6.0~,=AJ4A(4)<37.83/85.27>~6.0~,=AJ4SB(4)<35.82/86.33>~6.0~, + =AJ6T(4)<35.82/86.33>~6.0~,=AK4Z(4)<35.82/86.33>~6.0~,=AL4B(4)<35.82/86.33>~6.0~, + =AL4US(4)<35.82/86.33>~6.0~,=K0EJ(4)<35.82/86.33>~6.0~,=K0IE(4)<35.82/86.33>~6.0~, + =K0RGI(4)<35.82/86.33>~6.0~,=K1GU(4)<35.82/86.33>~6.0~,=K1MWH(4)<35.82/86.33>~6.0~, + =K1VX(4)<35.82/86.33>~6.0~,=K3IE(4)<35.82/86.33>~6.0~,=K3JWI(4)<35.82/86.33>~6.0~, + =K3OB(4)<37.83/85.27>~6.0~,=K4AB(4)<33.02/86.75>~6.0~,=K4AFE(4)<35.82/86.33>~6.0~, + =K4AL(4)<35.82/86.33>~6.0~,=K4AMC(4)<35.82/86.33>~6.0~,=K4AVX(4)<37.83/85.27>~6.0~, + =K4BFT(4)<33.02/86.75>~6.0~,=K4BWP(4)<37.83/85.27>~6.0~,=K4BX(4)<35.82/86.33>~6.0~, + =K4CNY(4)<33.02/86.75>~6.0~,=K4DR(4)<35.82/86.33>~6.0~,=K4DXV(4)<35.82/86.33>~6.0~, + =K4DZR(4)<35.82/86.33>~6.0~,=K4EES(4)<33.02/86.75>~6.0~,=K4FN(4)<37.83/85.27>~6.0~, + =K4GIG(4)<33.02/86.75>~6.0~,=K4HAL(4)<33.02/86.75>~6.0~,=K4IE(4)<37.83/85.27>~6.0~, =K4KCG(4)<37.83/85.27>~6.0~,=K4KO(4)<35.82/86.33>~6.0~,=K4KSV(4)<33.02/86.75>~6.0~, =K4KYN(4)<35.82/86.33>~6.0~,=K4LEN(4)<35.82/86.33>~6.0~,=K4NO(4)<33.02/86.75>~6.0~, =K4OAQ(4)<35.82/86.33>~6.0~,=K4PO(4)<33.02/86.75>~6.0~,=K4RO(4)<35.82/86.33>~6.0~, =K4SHA(4)<33.02/86.75>~6.0~,=K4SPO(4)<37.83/85.27>~6.0~,=K4TCG(4)<35.82/86.33>~6.0~, =K4TEC(4)<37.83/85.27>~6.0~,=K4TG(4)<37.83/85.27>~6.0~,=K4TZ(4)<37.83/85.27>~6.0~, =K4VIG(4)<35.82/86.33>~6.0~,=K4WG(4)<33.02/86.75>~6.0~,=K4WI(4)<33.02/86.75>~6.0~, - =K4WW(4)<37.83/85.27>~6.0~,=K4ZGB(4)<33.02/86.75>~6.0~,=K5VBA(4)<33.02/86.75>~6.0~, - =K5WP(4)<33.02/86.75>~6.0~,=K5ZQ(4)<37.83/85.27>~6.0~,=KA3MTT(4)<37.83/85.27>~6.0~, - =KA4J(4)<35.82/86.33>~6.0~,=KA4SCV(4)<35.82/86.33>~6.0~,=KA5VZG(4)<35.82/86.33>~6.0~, - =KB4QZH(4)<37.83/85.27>~6.0~,=KB4VL(4)<35.82/86.33>~6.0~,=KC1DWP(4)<35.82/86.33>~6.0~, - =KC4HW(4)<33.02/86.75>~6.0~,=KC4NX(4)<35.82/86.33>~6.0~,=KC4SAW(4)<35.82/86.33>~6.0~, + =K4WW(4)<37.83/85.27>~6.0~,=K4XXX(4)<37.83/85.27>~6.0~,=K4ZGB(4)<33.02/86.75>~6.0~, + =K5VBA(4)<33.02/86.75>~6.0~,=K5WP(4)<33.02/86.75>~6.0~,=K5ZQ(4)<37.83/85.27>~6.0~, + =KA3MTT(4)<37.83/85.27>~6.0~,=KA4J(4)<35.82/86.33>~6.0~,=KA4SCV(4)<35.82/86.33>~6.0~, + =KA5VZG(4)<35.82/86.33>~6.0~,=KB4QZH(4)<37.83/85.27>~6.0~,=KB4VL(4)<35.82/86.33>~6.0~, + =KC1DWP(4)<35.82/86.33>~6.0~,=KC4HW(4)<33.02/86.75>~6.0~,=KC4SAW(4)<35.82/86.33>~6.0~, =KC4TEO(4)<33.02/86.75>~6.0~,=KC4TNQ(4)<35.82/86.33>~6.0~,=KC4WQ(4)<37.83/85.27>~6.0~, - =KD4EE(4)<37.83/85.27>~6.0~,=KD4LT(4)<35.82/86.33>~6.0~,=KD7RF(4)<33.02/86.75>~6.0~, - =KE0L(4)<35.82/86.33>~6.0~,=KE4KY(4)<37.83/85.27>~6.0~,=KE4WE(4)<35.82/86.33>~6.0~, - =KF4AV(4)<37.83/85.27>~6.0~,=KF4FMQ(4)<37.83/85.27>~6.0~,=KF4RWA(4)<33.02/86.75>~6.0~, - =KF5MU(4)<37.83/85.27>~6.0~,=KF7CG(4)<35.82/86.33>~6.0~,=KG4CDL(4)<35.82/86.33>~6.0~, - =KG4CUY(4)<33.02/86.75>~6.0~,=KG4SRK(4)<37.83/85.27>~6.0~,=KG4STL(4)<35.82/86.33>~6.0~, + =KD4LT(4)<35.82/86.33>~6.0~,=KE0L(4)<35.82/86.33>~6.0~,=KE4KY(4)<37.83/85.27>~6.0~, + =KE4WE(4)<35.82/86.33>~6.0~,=KF4AV(4)<37.83/85.27>~6.0~,=KF4FMQ(4)<37.83/85.27>~6.0~, + =KF4RWA(4)<33.02/86.75>~6.0~,=KF5MU(4)<37.83/85.27>~6.0~,=KF7CG(4)<35.82/86.33>~6.0~, + =KG4CDL(4)<35.82/86.33>~6.0~,=KG4SRK(4)<37.83/85.27>~6.0~,=KG4STL(4)<35.82/86.33>~6.0~, =KG5HVO(4)<33.02/86.75>~6.0~,=KH2AR(4)[7]<35.82/86.33>~6.0~,=KH2GM(4)<33.02/86.75>~6.0~, =KI0EB(4)<37.83/85.27>~6.0~,=KJ4A(4)<37.83/85.27>~6.0~,=KJ4AOM(4)<37.83/85.27>~6.0~, - =KJ4M(4)<33.02/86.75>~6.0~,=KJ4ND(4)<37.83/85.27>~6.0~,=KJ4YLR(4)<37.83/85.27>~6.0~, - =KK2A(4)<35.82/86.33>~6.0~,=KK4JW(4)<37.83/85.27>~6.0~,=KL1SE(4)<37.83/85.27>~6.0~, - =KM4CH(4)<37.83/85.27>~6.0~,=KM4FO(4)<37.83/85.27>~6.0~,=KM4JAD(4)<35.82/86.33>~6.0~, - =KM4RO(4)<37.83/85.27>~6.0~,=KN4CNU(4)<35.82/86.33>~6.0~,=KN4LIU(4)<35.82/86.33>~6.0~, + =KJ4M(4)<33.02/86.75>~6.0~,=KJ4ND(4)<37.83/85.27>~6.0~,=KJ4UGO(4)<33.02/86.75>~6.0~, + =KJ4YLR(4)<37.83/85.27>~6.0~,=KJ6YXI(4)<35.82/86.33>~6.0~,=KK2A(4)<35.82/86.33>~6.0~, + =KK4JW(4)<37.83/85.27>~6.0~,=KM1Z(4)<35.82/86.33>~6.0~,=KM4CH(4)<37.83/85.27>~6.0~, + =KM4FO(4)<37.83/85.27>~6.0~,=KM4JAD(4)<35.82/86.33>~6.0~,=KM4RO(4)<37.83/85.27>~6.0~, + =KM4VI(4)<35.82/86.33>~6.0~,=KN4CNU(4)<35.82/86.33>~6.0~,=KN4LIU(4)<35.82/86.33>~6.0~, =KN4PHS(4)<33.02/86.75>~6.0~,=KO4AWC(4)<33.02/86.75>~6.0~,=KO4IFG(4)<35.82/86.33>~6.0~, =KO4OL(4)<37.83/85.27>~6.0~,=KO4PZX(4)<35.82/86.33>~6.0~,=KO4XJ(4)<37.83/85.27>~6.0~, =KO4YED(4)<35.82/86.33>~6.0~,=KO4Z(4)<35.82/86.33>~6.0~,=KP4X(4)<35.82/86.33>~6.0~, =KR4F(4)<33.02/86.75>~6.0~,=KS4X(4)<35.82/86.33>~6.0~,=KT4DW(4)<35.82/86.33>~6.0~, - =KT4O(4)<35.82/86.33>~6.0~,=KT4RH(4)<35.82/86.33>~6.0~,=KT4XA(4)<33.02/86.75>~6.0~, - =KU4A(4)<37.83/85.27>~6.0~,=KV4T(4)<33.02/86.75>~6.0~,=KW4J(4)<33.02/86.75>~6.0~, - =KW4LU(4)<35.82/86.33>~6.0~,=KW4SW(4)<33.02/86.75>~6.0~,=KW4YA(4)<37.83/85.27>~6.0~, - =KX4AV(4)<37.83/85.27>~6.0~,=KX4X(4)<33.02/86.75>~6.0~,=KY4BG(4)<37.83/85.27>~6.0~, - =KY4CU(4)<33.02/86.75>~6.0~,=KY4G(4)<33.02/86.75>~6.0~,=KY4HV(4)<33.02/86.75>~6.0~, - =KY4KY(4)<37.83/85.27>~6.0~,=KY4L(4)<35.82/86.33>~6.0~,=KZ4KX(4)<37.83/85.27>~6.0~, - =N4ARO(4)<35.82/86.33>~6.0~,=N4ATU(4)<35.82/86.33>~6.0~,=N4AU(4)<33.02/86.75>~6.0~, - =N4BCD(4)<35.82/86.33>~6.0~,=N4CSV(4)<35.82/86.33>~6.0~,=N4DW(4)<35.82/86.33>~6.0~, - =N4FR(4)<35.82/86.33>~6.0~,=N4IDH(4)<33.02/86.75>~6.0~,=N4JDB(4)<33.02/86.75>~6.0~, - =N4JKO(4)<37.83/85.27>~6.0~,=N4JR(4)<33.02/86.75>~6.0~,=N4JRG(4)<37.83/85.27>~6.0~, - =N4KGL(4)<33.02/86.75>~6.0~,=N4KH(4)<33.02/86.75>~6.0~,=N4LT(4)<35.82/86.33>~6.0~, - =N4MCC(4)<35.82/86.33>~6.0~,=N4MTE(4)<35.82/86.33>~6.0~,=N4MZ(4)<33.02/86.75>~6.0~, - =N4NA(4)<35.82/86.33>~6.0~,=N4NM(4)<33.02/86.75>~6.0~,=N4NP(4)<37.83/85.27>~6.0~, + =KT4E(4)<35.82/86.33>~6.0~,=KT4O(4)<35.82/86.33>~6.0~,=KT4RH(4)<35.82/86.33>~6.0~, + =KT4XA(4)<33.02/86.75>~6.0~,=KU4A(4)<37.83/85.27>~6.0~,=KU4H(4)<33.02/86.75>~6.0~, + =KV4T(4)<33.02/86.75>~6.0~,=KW4J(4)<33.02/86.75>~6.0~,=KW4LU(4)<35.82/86.33>~6.0~, + =KW4SW(4)<33.02/86.75>~6.0~,=KW4YA(4)<37.83/85.27>~6.0~,=KX4AV(4)<37.83/85.27>~6.0~, + =KX4X(4)<33.02/86.75>~6.0~,=KY4BG(4)<37.83/85.27>~6.0~,=KY4CU(4)<33.02/86.75>~6.0~, + =KY4G(4)<33.02/86.75>~6.0~,=KY4HV(4)<33.02/86.75>~6.0~,=KY4KY(4)<37.83/85.27>~6.0~, + =KY4L(4)<35.82/86.33>~6.0~,=KZ4KX(4)<37.83/85.27>~6.0~,=N4ARO(4)<35.82/86.33>~6.0~, + =N4ATU(4)<35.82/86.33>~6.0~,=N4AU(4)<33.02/86.75>~6.0~,=N4BCD(4)<35.82/86.33>~6.0~, + =N4CSV(4)<35.82/86.33>~6.0~,=N4DW(4)<35.82/86.33>~6.0~,=N4FR(4)<35.82/86.33>~6.0~, + =N4IDH(4)<33.02/86.75>~6.0~,=N4JDB(4)<33.02/86.75>~6.0~,=N4JKO(4)<37.83/85.27>~6.0~, + =N4JR(4)<33.02/86.75>~6.0~,=N4JRG(4)<37.83/85.27>~6.0~,=N4KGL(4)<33.02/86.75>~6.0~, + =N4KH(4)<33.02/86.75>~6.0~,=N4LT(4)<35.82/86.33>~6.0~,=N4MCC(4)<35.82/86.33>~6.0~, + =N4MTE(4)<35.82/86.33>~6.0~,=N4MZ(4)<33.02/86.75>~6.0~,=N4NA(4)<35.82/86.33>~6.0~, + =N4NM(4)<33.02/86.75>~6.0~,=N4NP(4)<37.83/85.27>~6.0~,=N4OPI(4)<35.82/86.33>~6.0~, =N4PX(4)<35.82/86.33>~6.0~,=N4QS(4)<37.83/85.27>~6.0~,=N4ROA(4)<37.83/85.27>~6.0~, - =N4RRR(4)<35.82/86.33>~6.0~,=N4SS(4)<37.83/85.27>~6.0~,=N4TY(4)<37.83/85.27>~6.0~, - =N4UC(4)<33.02/86.75>~6.0~,=N4UW(4)<35.82/86.33>~6.0~,=N4VN(4)<33.02/86.75>~6.0~, - =N4WE(4)<33.02/86.75>~6.0~,=N4XFF(4)<37.83/85.27>~6.0~,=N4ZZ(4)<35.82/86.33>~6.0~, - =N6MA(4)<35.82/86.33>~6.0~,=N7CR(4)<33.02/86.75>~6.0~,=N7LCT(4)<35.82/86.33>~6.0~, - =NA4A(4)<33.02/86.75>~6.0~,=NA4C(4)<35.82/86.33>~6.0~,=NA4MM(4)<33.02/86.75>~6.0~, - =NA4W(4)<33.02/86.75>~6.0~,=ND4X(4)<37.83/85.27>~6.0~,=ND4Y(4)<37.83/85.27>~6.0~, + =N4RRR(4)<35.82/86.33>~6.0~,=N4RZ(4)<37.83/85.27>~6.0~,=N4SS(4)<37.83/85.27>~6.0~, + =N4TY(4)<37.83/85.27>~6.0~,=N4UC(4)<33.02/86.75>~6.0~,=N4UL(4)<37.83/85.27>~6.0~, + =N4UW(4)<35.82/86.33>~6.0~,=N4VN(4)<33.02/86.75>~6.0~,=N4WE(4)<33.02/86.75>~6.0~, + =N4ZZ(4)<35.82/86.33>~6.0~,=N6MA(4)<35.82/86.33>~6.0~,=N7CR(4)<33.02/86.75>~6.0~, + =N7KDT(4)<33.02/86.75>~6.0~,=N7LCT(4)<35.82/86.33>~6.0~,=NA4A(4)<33.02/86.75>~6.0~, + =NA4C(4)<35.82/86.33>~6.0~,=NA4MM(4)<33.02/86.75>~6.0~,=NA4W(4)<33.02/86.75>~6.0~, + =ND4U(4)<35.82/86.33>~6.0~,=ND4X(4)<37.83/85.27>~6.0~,=ND4Y(4)<37.83/85.27>~6.0~, =NE5W(4)<33.02/86.75>~6.0~,=NF4J(4)<33.02/86.75>~6.0~,=NF7D(4)<33.02/86.75>~6.0~, =NH6T(4)<33.02/86.75>~6.0~,=NJ4P(4)<35.82/86.33>~6.0~,=NK4I(4)<35.82/86.33>~6.0~, =NN4NT(4)<33.02/86.75>~6.0~,=NN4SA(4)<33.02/86.75>~6.0~,=NN5SS(4)<35.82/86.33>~6.0~, =NP3FB(4)<33.02/86.75>~6.0~,=NR4L(4)<35.82/86.33>~6.0~,=NS4X(4)<35.82/86.33>~6.0~, =NT4J(4)<35.82/86.33>~6.0~,=NT4W(4)<37.83/85.27>~6.0~,=NU4B(4)<35.82/86.33>~6.0~, - =NU4N(4)<37.83/85.27>~6.0~,=NV4B(4)<33.02/86.75>~6.0~,=W0AG(4)<35.82/86.33>~6.0~, - =W0SK(4)<35.82/86.33>~6.0~,=W2BJN(4)<33.02/86.75>~6.0~,=W3TB(4)<35.82/86.33>~6.0~, - =W4AWE(4)<35.82/86.33>~6.0~,=W4BCG(4)<35.82/86.33>~6.0~,=W4CMG(4)<35.82/86.33>~6.0~, - =W4CQE(4)<35.82/86.33>~6.0~,=W4DAN(4)<35.82/86.33>~6.0~,=W4DTA(4)<35.82/86.33>~6.0~, - =W4ER(4)<33.02/86.75>~6.0~,=W4ETA(4)<35.82/86.33>~6.0~,=W4GKM(4)<35.82/86.33>~6.0~, - =W4GON(4)<35.82/86.33>~6.0~,=W4IL(4)<35.82/86.33>~6.0~,=W4IOD(4)<37.83/85.27>~6.0~, - =W4JH(4)<35.82/86.33>~6.0~,=W4KW(4)<35.82/86.33>~6.0~,=W4LC(4)<37.83/85.27>~6.0~, - =W4NBS(4)<33.02/86.75>~6.0~,=W4NI(4)<35.82/86.33>~6.0~,=W4NJA(4)<37.83/85.27>~6.0~, - =W4NNF(4)<33.02/86.75>~6.0~,=W4NZ(4)<35.82/86.33>~6.0~,=W4PA(4)<35.82/86.33>~6.0~, - =W4PF(4)<37.83/85.27>~6.0~,=W4RLB(4)<37.83/85.27>~6.0~,=W4RYW(4)<33.02/86.75>~6.0~, - =W4SDX(4)<35.82/86.33>~6.0~,=W4SIG(4)<35.82/86.33>~6.0~,=W4SK(4)<35.82/86.33>~6.0~, - =W4SSF(4)<35.82/86.33>~6.0~,=W4TLK(4)<35.82/86.33>~6.0~,=W4UAL(4)<33.02/86.75>~6.0~, - =W4UT(4)<35.82/86.33>~6.0~,=W4UWC(4)<37.83/85.27>~6.0~,=W4VFF(4)<35.82/86.33>~6.0~, - =W4WKU(4)<37.83/85.27>~6.0~,=W4XK(4)<35.82/86.33>~6.0~,=W4ZXT(4)<37.83/85.27>~6.0~, - =W5ACQ(4)<35.82/86.33>~6.0~,=W5MX(4)<37.83/85.27>~6.0~,=W5NZ(4)<33.02/86.75>~6.0~, - =W5PET(4)<37.83/85.27>~6.0~,=W6NCB(4)<35.82/86.33>~6.0~,=W6UB(4)<35.82/86.33>~6.0~, + =NU4N(4)<37.83/85.27>~6.0~,=NV4B(4)<33.02/86.75>~6.0~,=NV4G(4)<35.82/86.33>~6.0~, + =W0AG(4)<35.82/86.33>~6.0~,=W0SK(4)<35.82/86.33>~6.0~,=W2BJN(4)<33.02/86.75>~6.0~, + =W3TB(4)<35.82/86.33>~6.0~,=W4AWE(4)<35.82/86.33>~6.0~,=W4BCG(4)<35.82/86.33>~6.0~, + =W4CMG(4)<35.82/86.33>~6.0~,=W4CQE(4)<35.82/86.33>~6.0~,=W4DAN(4)<35.82/86.33>~6.0~, + =W4DTA(4)<35.82/86.33>~6.0~,=W4ER(4)<33.02/86.75>~6.0~,=W4ETA(4)<35.82/86.33>~6.0~, + =W4GKM(4)<35.82/86.33>~6.0~,=W4GON(4)<35.82/86.33>~6.0~,=W4IL(4)<35.82/86.33>~6.0~, + =W4IOD(4)<37.83/85.27>~6.0~,=W4JH(4)<35.82/86.33>~6.0~,=W4JUU(4)<35.82/86.33>~6.0~, + =W4KW(4)<35.82/86.33>~6.0~,=W4LC(4)<37.83/85.27>~6.0~,=W4NBS(4)<33.02/86.75>~6.0~, + =W4NI(4)<35.82/86.33>~6.0~,=W4NJA(4)<37.83/85.27>~6.0~,=W4NNF(4)<33.02/86.75>~6.0~, + =W4NZ(4)<35.82/86.33>~6.0~,=W4PA(4)<35.82/86.33>~6.0~,=W4PF(4)<37.83/85.27>~6.0~, + =W4RLB(4)<37.83/85.27>~6.0~,=W4RYW(4)<33.02/86.75>~6.0~,=W4SDX(4)<35.82/86.33>~6.0~, + =W4SIG(4)<35.82/86.33>~6.0~,=W4SK(4)<35.82/86.33>~6.0~,=W4SSF(4)<35.82/86.33>~6.0~, + =W4TLK(4)<35.82/86.33>~6.0~,=W4UAL(4)<33.02/86.75>~6.0~,=W4UT(4)<35.82/86.33>~6.0~, + =W4UWC(4)<37.83/85.27>~6.0~,=W4VFF(4)<35.82/86.33>~6.0~,=W4WKU(4)<37.83/85.27>~6.0~, + =W4WTS(4)<35.82/86.33>~6.0~,=W4WWV(4)<35.82/86.33>~6.0~,=W4XK(4)<35.82/86.33>~6.0~, + =W4YTO(4)<33.02/86.75>~6.0~,=W4ZXT(4)<37.83/85.27>~6.0~,=W5ACQ(4)<35.82/86.33>~6.0~, + =W5MX(4)<37.83/85.27>~6.0~,=W5NZ(4)<33.02/86.75>~6.0~,=W5PET(4)<37.83/85.27>~6.0~, + =W6LX(4)<37.83/85.27>~6.0~,=W6NCB(4)<35.82/86.33>~6.0~,=W6UB(4)<35.82/86.33>~6.0~, =WA1FCN(4)<33.02/86.75>~6.0~,=WA4GGK(4)<33.02/86.75>~6.0~,=WA4HR(4)<35.82/86.33>~6.0~, - =WA4JA(4)<35.82/86.33>~6.0~,=WA4JQS(4)<37.83/85.27>~6.0~,=WA4VEK(4)<33.02/86.75>~6.0~, - =WA4WZR(4)<35.82/86.33>~6.0~,=WA4ZXV(4)<33.02/86.75>~6.0~,=WA5POK(4)<35.82/86.33>~6.0~, - =WB0CJB(4)<35.82/86.33>~6.0~,=WB4E(4)<35.82/86.33>~6.0~,=WB4HMA(4)<35.82/86.33>~6.0~, + =WA4JA(4)<35.82/86.33>~6.0~,=WA4JQS(4)<37.83/85.27>~6.0~,=WA4KY(4)<37.83/85.27>~6.0~, + =WA4VEK(4)<33.02/86.75>~6.0~,=WA4VGZ(4)<35.82/86.33>~6.0~,=WA4WZR(4)<35.82/86.33>~6.0~, + =WA4ZXV(4)<33.02/86.75>~6.0~,=WA5POK(4)<35.82/86.33>~6.0~,=WB0CJB(4)<35.82/86.33>~6.0~, + =WB4E(4)<35.82/86.33>~6.0~,=WB4GHZ(4)<35.82/86.33>~6.0~,=WB4HMA(4)<35.82/86.33>~6.0~, =WB4IT(4)<33.02/86.75>~6.0~,=WB4WXE(4)<33.02/86.75>~6.0~,=WB4YDL(4)<35.82/86.33>~6.0~, =WB4YDY(4)<35.82/86.33>~6.0~,=WB5WAJ(4)<35.82/86.33>~6.0~,=WD4CFN(4)<35.82/86.33>~6.0~, =WD4OHD(4)<35.82/86.33>~6.0~,=WD4PTJ(4)<35.82/86.33>~6.0~,=WE5P(4)<37.83/85.27>~6.0~, @@ -3237,26 +3259,30 @@ United States: 05: 08: NA: 37.60: 91.87: 5.0: K: =WX4HP(4)<33.02/86.75>~6.0~,=WX4W(4)<37.83/85.27>~6.0~,=WZ4F(4)<33.02/86.75>~6.0~, # MT-WY =AC7AF(4)[7]<42.70/106.98>~7.0~,=AE7AP(4)[6]<46.77/111.32>~7.0~,=AL7RT(4)[6]<46.77/111.32>~7.0~, - =K0SN(4)[6]<46.77/111.32>~7.0~,=K7ABV(4)[6]<46.77/111.32>~7.0~,=K7QA(4)[6]<46.77/111.32>~7.0~, - =K7SHR(4)[7]<42.70/106.98>~7.0~,=K7VU(4)[7]<42.70/106.98>~7.0~,=K9DR(4)[7]<42.70/106.98>~7.0~, - =KD7HU(4)[7]<46.77/111.32>~7.0~,=KF7Z(4)[7]<42.70/106.98>~7.0~,=KI6QDH(4)[7]<42.70/106.98>~7.0~, - =KJ9C(4)[6]<46.77/111.32>~7.0~,=KM7W(4)[6]<46.77/111.32>~7.0~,=KS7T(4)[6]<46.77/111.32>~7.0~, - =KT0P(4)[6]<46.77/111.32>~7.0~,=N7MZW(4)[7]<42.70/106.98>~7.0~,=N7QAX(4)[7]<42.70/106.98>~7.0~, + =K0SN(4)[6]<46.77/111.32>~7.0~,=K5MH(4)[6]<46.77/111.32>~7.0~,=K7ABV(4)[6]<46.77/111.32>~7.0~, + =K7MTD(4)[6]<46.77/111.32>~7.0~,=K7QA(4)[6]<46.77/111.32>~7.0~,=K7SHR(4)[7]<42.70/106.98>~7.0~, + =K7VU(4)[7]<42.70/106.98>~7.0~,=K9DR(4)[7]<42.70/106.98>~7.0~,=KD7HU(4)[7]<46.77/111.32>~7.0~, + =KF7Z(4)[7]<42.70/106.98>~7.0~,=KI6QDH(4)[7]<42.70/106.98>~7.0~,=KJ9C(4)[6]<46.77/111.32>~7.0~, + =KM7W(4)[6]<46.77/111.32>~7.0~,=KR7Q(4)[6]<46.77/111.32>~7.0~,=KS7T(4)[6]<46.77/111.32>~7.0~, + =KT0P(4)[6]<46.77/111.32>~7.0~,=N7AGP(4)[6]<46.77/111.32>~7.0~,=N7CMJ(4)[6]<46.77/111.32>~7.0~, + =N7MZW(4)[7]<42.70/106.98>~7.0~,=N7PMS(4)[6]<46.77/111.32>~7.0~,=N7QAX(4)[7]<42.70/106.98>~7.0~, =N7WY(4)[7]<42.70/106.98>~7.0~,=N9RV(4)[6]<46.77/111.32>~7.0~,=NR7DX(4)[6]<46.77/111.32>~7.0~, - =W6KGP(4)[7]<42.70/106.98>~7.0~,=W7DGR(4)[7]<42.70/106.98>~7.0~,=W7EE(4)[6]<46.77/111.32>~7.0~, - =W7EY(4)[6]<46.77/111.32>~7.0~,=W7KK(4)[6]<46.77/111.32>~7.0~,=W7RIP(4)[6]<46.77/111.32>~7.0~, - =W7VNE(4)[6]<46.77/111.32>~7.0~,=W7XT(4)[6]<46.77/111.32>~7.0~,=W8MYL(4)[7]<42.70/106.98>~7.0~, - =WB6UIA(4)[7]<42.70/106.98>~7.0~,=WB7S(4)[7]<42.70/106.98>~7.0~,=WC7S(4)[7]<42.70/106.98>~7.0~, - =WN7Y(4)[7]<46.77/111.32>~7.0~,=WY7DT(4)[7]<42.70/106.98>~7.0~,=WY7FD(4)[7]<42.70/106.98>~7.0~, - =WY7M(4)[7]<42.70/106.98>~7.0~, + =NX7W(4)[6]<46.77/111.32>~7.0~,=W6KGP(4)[7]<42.70/106.98>~7.0~,=W7DGR(4)[7]<42.70/106.98>~7.0~, + =W7EE(4)[6]<46.77/111.32>~7.0~,=W7EY(4)[6]<46.77/111.32>~7.0~,=W7KK(4)[6]<46.77/111.32>~7.0~, + =W7RIP(4)[6]<46.77/111.32>~7.0~,=W7VNE(4)[6]<46.77/111.32>~7.0~,=W7XT(4)[6]<46.77/111.32>~7.0~, + =W8MYL(4)[7]<42.70/106.98>~7.0~,=WA1TYB(4)[6]<46.77/111.32>~7.0~,=WB6UIA(4)[7]<42.70/106.98>~7.0~, + =WB7GR(4)[7]<42.70/106.98>~7.0~,=WB7S(4)[7]<42.70/106.98>~7.0~,=WC7S(4)[7]<42.70/106.98>~7.0~, + =WN7Y(4)[7]<46.77/111.32>~7.0~,=WX7F(4)[6]<46.77/111.32>~7.0~,=WY7DT(4)[7]<42.70/106.98>~7.0~, + =WY7FD(4)[7]<42.70/106.98>~7.0~,=WY7M(4)[7]<42.70/106.98>~7.0~, # West Virginia =AA8UL<38.82/80.67>,=AB8RL<38.82/80.67>,=K8DF<38.82/80.67>,=K8HC<38.82/80.67>,=K8JQ<38.82/80.67>, =K8NYG<38.82/80.67>,=K8RRT<38.82/80.67>,=K8WV<38.82/80.67>,=KA8NJW<38.82/80.67>, =KB8DX<38.82/80.67>,=KB8KMH<38.82/80.67>,=KE8AE<38.82/80.67>,=KI8I<38.82/80.67>, =N8BL<38.82/80.67>,=N8II<38.82/80.67>,=N8UOA<38.82/80.67>,=N8ZR<38.82/80.67>,=NR5N<38.82/80.67>, - =NW8U<38.82/80.67>,=W8AKS<38.82/80.67>,=W8BAR<38.82/80.67>,=W8OP<38.82/80.67>,=W8SP<38.82/80.67>, - =W8WEJ<38.82/80.67>,=W8WVA<38.82/80.67>,=WA8KAN<38.82/80.67>,=WB8III<38.82/80.67>, - =WJ8L<38.82/80.67>,=WR8AA<38.82/80.67>,=WT8WV<38.82/80.67>,=WW8RT<38.82/80.67>; + =NW8U<38.82/80.67>,=W8AKS<38.82/80.67>,=W8BAR<38.82/80.67>,=W8OP<38.82/80.67>,=W8RKW<38.82/80.67>, + =W8SP<38.82/80.67>,=W8WEJ<38.82/80.67>,=W8WVA<38.82/80.67>,=WA8KAN<38.82/80.67>, + =WB8III<38.82/80.67>,=WC8L<38.82/80.67>,=WJ8L<38.82/80.67>,=WR8AA<38.82/80.67>, + =WT8WV<38.82/80.67>; # ADIF 105 Guantanamo Bay: 08: 11: NA: 20.00: 75.00: 5.0: KG4: KG4,=KG4NE; @@ -3268,7 +3294,8 @@ Baker & Howland Islands: 31: 61: OC: 0.00: 176.00: 12.0: KH1: AH1,KH1,NH1,WH1; # ADIF 103 Guam: 27: 64: OC: 13.37: -144.70: -10.0: KH2: - AH2,KH2,NH2,WH2,=KA1I,=KF7BMU,=KF7GTT,=KG6DX,=KG6JDX,=KH0P,=KO4EGC,=KO4JB,=N1GU,=N6MSK,=WA0OII; + AH2,KH2,NH2,WH2,=KA1I,=KF7BMU,=KF7GTT,=KG6DX,=KG6JDX,=KH0P,=KO4EGC,=KO4JB,=N1GU,=N6MSK,=N6WBN, + =WA0OII; # ADIF 123 Johnston Island: 31: 61: OC: 16.72: 169.53: 10.0: KH3: AH3,KH3,NH3,WH3; @@ -3280,10 +3307,10 @@ Palmyra & Jarvis Islands: 31: 61: OC: 5.87: 162.07: 11.0: KH5: AH5,KH5,NH5,WH5; # ADIF 110 Hawaii: 31: 61: OC: 21.12: 157.48: 10.0: KH6: - AH6,AH7,KH6,KH7,NH6,NH7,WH6,WH7,=AA3CM,=AA7DI,=AB7RT,=AE7QR,=K0RIV,=K2GT,=K4RBK,=K4XV,=K5ZYO, - =K7DWJ,=K7WTH,=KA6MCS,=KB3WLK,=KB6EGA,=KB6LDF,=KB7HN,=KC6REO,=KC8JNV,=KC9CVX,=KD0OLJ,=KD9IAH, - =KD9TEZ,=KE7HKK,=KE8QB,=KG4TML,=KG5HMM,=KG7VWF,=KH0WJ,=KH3AE,=KI7NAU,=KJ6THQ,=KJ6THS,=KK4KPX, - =KK4KXS,=KK4QAX,=KN6CID,=KN6NAH,=KO4LNN,=KZ6T,=N3GWR,=N6OM,=N7AK,=W1ZWC,=W2DK,=W6KCH,=W6SPO,=W6W, + AH6,AH7,KH6,KH7,NH6,NH7,WH6,WH7,=AA2TT,=AA3CM,=AA7DI,=AB7RT,=K0RIV,=K2GT,=K4RBK,=K4XV,=K5ZYO, + =K7DWJ,=K7WTH,=KA6MCS,=KB6EGA,=KB6LDF,=KC6REO,=KC8JNV,=KC9CVX,=KD0OLJ,=KD9IAH,=KE7HKK,=KE8QB, + =KG4TML,=KG5HMM,=KG6JSH,=KG7VWF,=KH0WJ,=KH3AE,=KI7NAU,=KJ6THQ,=KJ6THS,=KK4KPX,=KK4KXS,=KK4QAX, + =KM6NFS,=KN6CID,=KN6NAH,=KO4LNN,=KO4ZYO,=KX4ES,=KZ6T,=N3GWR,=N6OM,=W1ZWC,=W2DK,=W6KCH,=W6SPO,=W6W, =W8EBR,=WA6QDQ,=WB2CAT,=WB4JTT,=WG1A; # ADIF 138 Kure Island: 31: 61: OC: 29.00: 178.00: 10.0: KH7K: @@ -3299,20 +3326,20 @@ Wake Island: 31: 65: OC: 19.28: -166.63: -12.0: KH9: AH9,KH9,NH9,WH9; # ADIF 6 Alaska: 01: 01: NA: 61.40: 148.87: 8.0: KL: - AL,KL,NL,WL,=AC2UJ,=AE6FK,=AI4SV,=K6VH,=K8VEZ,=K9UL,=K9YJW,=KA1NCN,=KA3FZH,=KB1UPV,=KD0OXY, - =KD7CTV,=KD7WSQ,=KD8RMO,=KE0PRX,=KE6FNQ,=KG4SUE,=KG5HMN,=KG7MIK,=KI5HPI,=KI6WBS,=KJ6GDW,=KJ7FOI, - =KJ7OKC,=KJ7YTO,=KK6NRF,=KK7BVD,=KK7I,=KN6JOY,=KN6LTY,=N1TX,=N2KJU,=N4IJB,=N5UC,=N6JM,=N7CGC, - =N7XBY,=N7ZXJ,=NA9Z,=NN7C,=W4BZB,=W7ORR,=WA3LEE; + AL,KL,NL,WL,=AC2UJ,=AD0DK,=AE6FK,=AI4SV,=K6VH,=K8VEZ,=K9UL,=K9YJW,=KA1NCN,=KA2ZSD,=KA3FZH,=KB1UPV, + =KD0OXY,=KD7CTV,=KD7WSQ,=KD8RMO,=KE0PRX,=KE6FNQ,=KG4SUE,=KG7AUD,=KG7MIK,=KI5HPI,=KI5KBN,=KI6WBS, + =KJ6EHV,=KJ6GDW,=KJ7FOI,=KJ7OKC,=KJ7YTO,=KK6NRF,=KK7BVD,=KK7I,=KN6JOY,=KN6LTY,=N1TX,=N2KJU,=N4IJB, + =N5UC,=N6JM,=N7CGC,=N7XBY,=N7ZXJ,=NA9Z,=NN7C,=W4BZB,=W7ORR,=WA3LEE; # ADIF 182 Navassa Island: 08: 11: NA: 18.40: 75.00: 5.0: KP1: KP1,NP1,WP1; # ADIF 285 US Virgin Islands: 08: 11: NA: 17.73: 64.80: 4.0: KP2: - KP2,NP2,WP2,=K9VV,=KD4CDL,=KV4BT,=N8V,=W7YDT; + KP2,NP2,WP2,=K9VV,=KD4CDL,=KV4BT,=N8V,=W4LIS,=W7YDT; # ADIF 202 Puerto Rico: 08: 11: NA: 18.18: 66.55: 4.0: KP4: - KP3,KP4,NP3,NP4,WP3,WP4,=AL7KI,=K1QM,=K4W,=KA3PNP,=KB2FJY,=KD2BK,=KD2VEC,=KH2RU,=KI5MKH,=KK4GKZ, - =KK4HLB,=KN4GYF,=KP2H,=KP2Z,=N5YXK,=N8TJH,=WB2HMY; + KP3,KP4,NP3,NP4,WP3,WP4,=AL7KI,=K1QM,=K4W,=KA3PNP,=KD2BK,=KD2VEC,=KH2RU,=KI5MKH,=KK4GKZ,=KK4HLB, + =KN4GYF,=KP2H,=KP2Z,=N5YIZ,=N5YXK,=N8TJH,=WB2HMY; # ADIF 43 Desecheo Island: 08: 11: NA: 18.08: 67.88: 4.0: KP5: KP5,NP5,WP5; @@ -3514,9 +3541,9 @@ Argentina: 13: 14: SA: -32.50: 62.13: 3.0: LU: LW2E<-33.70/61.00>,LW3D<-33.70/61.00>,LW3E<-33.70/61.00>,LW4D<-33.70/61.00>,LW4E<-33.70/61.00>, LW5D<-33.70/61.00>,LW5E<-33.70/61.00>,LW6D<-33.70/61.00>,LW6E<-33.70/61.00>,LW7D<-33.70/61.00>, LW7E<-33.70/61.00>,LW8D<-33.70/61.00>,LW8E<-33.70/61.00>,LW9D<-33.70/61.00>,LW9E<-33.70/61.00>, - =LU1COP/E<-33.70/61.00>,=LU3DK/D<-33.70/61.00>,=LU3DY/D<-33.70/61.00>,=LU4AAO/D<-33.70/61.00>, - =LU4DX/D<-33.70/61.00>,=LU7CC/E<-33.70/61.00>,=LU7DZV/D<-33.70/61.00>,=LU7IRS/D<-33.70/61.00>, - =LW1ECO/D<-33.70/61.00>,=LW8DRU/D<-33.70/61.00>, + =LU2CJV/D<-33.70/61.00>,=LU4ARU/D<-33.70/61.00>,=LU4BAN/D<-33.70/61.00>,=LU4DX/D<-33.70/61.00>, + =LU5BBV/D<-33.70/61.00>,=LU7CC/E<-33.70/61.00>,=LU7DZV/D<-33.70/61.00>,=LU8ALE/D<-33.70/61.00>, + =LW8DRU/D<-33.70/61.00>, # Santa Fe AY0F<-33.72/62.23>,AY1F<-33.72/62.23>,AY2F<-33.72/62.23>,AY3F<-33.72/62.23>,AY4F<-33.72/62.23>, AY5F<-33.72/62.23>,AY6F<-33.72/62.23>,AY7F<-33.72/62.23>,AY8F<-33.72/62.23>,AY9F<-33.72/62.23>, @@ -3556,7 +3583,6 @@ Argentina: 13: 14: SA: -32.50: 62.13: 3.0: LU: LV5F<-33.72/62.23>,LV6F<-33.72/62.23>,LV7F<-33.72/62.23>,LV8F<-33.72/62.23>,LV9F<-33.72/62.23>, LW0F<-33.72/62.23>,LW1F<-33.72/62.23>,LW2F<-33.72/62.23>,LW3F<-33.72/62.23>,LW4F<-33.72/62.23>, LW5F<-33.72/62.23>,LW6F<-33.72/62.23>,LW7F<-33.72/62.23>,LW8F<-33.72/62.23>,LW9F<-33.72/62.23>, - =LU1FMC/F<-33.72/62.23>, # Chaco and Formosa AY0G<-26.95/58.72>,AY1G<-26.95/58.72>,AY2G<-26.95/58.72>,AY3G<-26.95/58.72>,AY4G<-26.95/58.72>, AY5G<-26.95/58.72>,AY6G<-26.95/58.72>,AY7G<-26.95/58.72>,AY8G<-26.95/58.72>,AY9G<-26.95/58.72>, @@ -4144,7 +4170,6 @@ Argentina: 13: 14: SA: -32.50: 62.13: 3.0: LU: LV5U<-36.62/64.28>,LV6U<-36.62/64.28>,LV7U<-36.62/64.28>,LV8U<-36.62/64.28>,LV9U<-36.62/64.28>, LW0U<-36.62/64.28>,LW1U<-36.62/64.28>,LW2U<-36.62/64.28>,LW3U<-36.62/64.28>,LW4U<-36.62/64.28>, LW5U<-36.62/64.28>,LW6U<-36.62/64.28>,LW7U<-36.62/64.28>,LW8U<-36.62/64.28>,LW9U<-36.62/64.28>, - =LU2AMM/U<-36.62/64.28>,=LU4BMG/U<-36.62/64.28>, # Rio Negro AY0V[16]<-40.80/63.00>,AY1V[16]<-40.80/63.00>,AY2V[16]<-40.80/63.00>,AY3V[16]<-40.80/63.00>, AY4V[16]<-40.80/63.00>,AY5V[16]<-40.80/63.00>,AY6V[16]<-40.80/63.00>,AY7V[16]<-40.80/63.00>, @@ -4193,8 +4218,7 @@ Argentina: 13: 14: SA: -32.50: 62.13: 3.0: LU: LV6V[16]<-40.80/63.00>,LV7V[16]<-40.80/63.00>,LV8V[16]<-40.80/63.00>,LV9V[16]<-40.80/63.00>, LW0V[16]<-40.80/63.00>,LW1V[16]<-40.80/63.00>,LW2V[16]<-40.80/63.00>,LW3V[16]<-40.80/63.00>, LW4V[16]<-40.80/63.00>,LW5V[16]<-40.80/63.00>,LW6V[16]<-40.80/63.00>,LW7V[16]<-40.80/63.00>, - LW8V[16]<-40.80/63.00>,LW9V[16]<-40.80/63.00>,=LU1VDF/V[16]<-40.80/63.00>, - =LU3VAL/V[16]<-40.80/63.00>, + LW8V[16]<-40.80/63.00>,LW9V[16]<-40.80/63.00>,=LU9VEA/V[16]<-40.80/63.00>, # Chubut AY0W[16]<-43.30/65.10>,AY1W[16]<-43.30/65.10>,AY2W[16]<-43.30/65.10>,AY3W[16]<-43.30/65.10>, AY4W[16]<-43.30/65.10>,AY5W[16]<-43.30/65.10>,AY6W[16]<-43.30/65.10>,AY7W[16]<-43.30/65.10>, @@ -4243,7 +4267,7 @@ Argentina: 13: 14: SA: -32.50: 62.13: 3.0: LU: LV6W[16]<-43.30/65.10>,LV7W[16]<-43.30/65.10>,LV8W[16]<-43.30/65.10>,LV9W[16]<-43.30/65.10>, LW0W[16]<-43.30/65.10>,LW1W[16]<-43.30/65.10>,LW2W[16]<-43.30/65.10>,LW3W[16]<-43.30/65.10>, LW4W[16]<-43.30/65.10>,LW5W[16]<-43.30/65.10>,LW6W[16]<-43.30/65.10>,LW7W[16]<-43.30/65.10>, - LW8W[16]<-43.30/65.10>,LW9W[16]<-43.30/65.10>,=LU2WA/W[16]<-43.30/65.10>, + LW8W[16]<-43.30/65.10>,LW9W[16]<-43.30/65.10>, # Santa Cruz & Tierra del Fuego AY0X[16]<-50.43/69.25>,AY1X[16]<-50.43/69.25>,AY2X[16]<-50.43/69.25>,AY3X[16]<-50.43/69.25>, AY4X[16]<-50.43/69.25>,AY5X[16]<-50.43/69.25>,AY6X[16]<-50.43/69.25>,AY7X[16]<-50.43/69.25>, @@ -4378,7 +4402,7 @@ Lebanon: 20: 39: AS: 33.83: -35.83: -2.0: OD: OD; # ADIF 206 Austria: 15: 28: EU: 47.33: -13.33: -1.0: OE: - OE,=4U0R,=4U1A,=4U1VIC,=4U1XMAS,=4U2U,=4Y1A,=C7A, + OE,=4U0R,=4U1A,=4U1VIC,=4U2U,=4Y1A,=C7A, # Vienna OE1<48.22/-16.37>, # Salzburg @@ -4746,7 +4770,7 @@ Palau: 27: 64: OC: 7.45: -134.53: -9.0: T8: T8; # ADIF 390 Asiatic Turkey: 20: 39: AS: 39.18: -35.65: -2.0: TA: - TA,TB,TC,YM, + TA,TB,TC,YM,=TC100TA,=TC100TC,=TC100TR,=TC100YEAR,=TC10O, # Turkish Islands TA0<38.95/-26.58>,TB0<38.95/-26.58>,TC0<38.95/-26.58>,YM0<38.95/-26.58>, # Nortwestern Anatolia @@ -4882,7 +4906,7 @@ European Russia: 16: 29: EU: 53.65: -41.37: -4.0: UA: UA1D<60.05/-31.75>,UB1C<60.05/-31.75>,UB1D<60.05/-31.75>,UC1C<60.05/-31.75>,UC1D<60.05/-31.75>, UD1C<60.05/-31.75>,UD1D<60.05/-31.75>,UE1C<60.05/-31.75>,UE1D<60.05/-31.75>,UF1C<60.05/-31.75>, UF1D<60.05/-31.75>,UG1C<60.05/-31.75>,UG1D<60.05/-31.75>,UH1C<60.05/-31.75>,UH1D<60.05/-31.75>, - UI1C<60.05/-31.75>,UI1D<60.05/-31.75>,=UA9CTT/P<60.05/-31.75>, + UI1C<60.05/-31.75>,UI1D<60.05/-31.75>, # Karelia (KL) R1N[19]<63.82/-33.00>,RA1N[19]<63.82/-33.00>,RC1N[19]<63.82/-33.00>,RD1N[19]<63.82/-33.00>, RE1N[19]<63.82/-33.00>,RF1N[19]<63.82/-33.00>,RG1N[19]<63.82/-33.00>,RJ1N[19]<63.82/-33.00>, @@ -5011,7 +5035,7 @@ European Russia: 16: 29: EU: 53.65: -41.37: -4.0: UA: UG3C<55.75/-37.62>,UG5A<55.75/-37.62>,UG5B<55.75/-37.62>,UG5C<55.75/-37.62>,UH3A<55.75/-37.62>, UH3B<55.75/-37.62>,UH3C<55.75/-37.62>,UH5A<55.75/-37.62>,UH5B<55.75/-37.62>,UH5C<55.75/-37.62>, UI3A<55.75/-37.62>,UI3B<55.75/-37.62>,UI3C<55.75/-37.62>,UI5A<55.75/-37.62>,UI5B<55.75/-37.62>, - UI5C<55.75/-37.62>,=R1941OM<55.75/-37.62>,=UA3LMR/P<55.75/-37.62>,=UA9QCP/3<55.75/-37.62>, + UI5C<55.75/-37.62>,=R1941OM<55.75/-37.62>,=UA9QCP/3<55.75/-37.62>, # Moskovskaya (MO) R2D<55.70/-36.97>,R2H<55.70/-36.97>,R3D<55.70/-36.97>,R3F<55.70/-36.97>,R3H<55.70/-36.97>, R5D<55.70/-36.97>,R5F<55.70/-36.97>,R5H<55.70/-36.97>,RA3D<55.70/-36.97>,RA3F<55.70/-36.97>, @@ -5248,6 +5272,7 @@ European Russia: 16: 29: EU: 53.65: -41.37: -4.0: UA: UG5K<51.05/-40.15>,UG5O<51.05/-40.15>,UG5Q<51.05/-40.15>,UH3K<51.05/-40.15>,UH3O<51.05/-40.15>, UH3Q<51.05/-40.15>,UH5K<51.05/-40.15>,UH5O<51.05/-40.15>,UH5Q<51.05/-40.15>,UI3K<51.05/-40.15>, UI3O<51.05/-40.15>,UI3Q<51.05/-40.15>,UI5K<51.05/-40.15>,UI5O<51.05/-40.15>,UI5Q<51.05/-40.15>, + =RA80OW<51.05/-40.15>,=RN80OW<51.05/-40.15>,=RO80OW<51.05/-40.15>,=RU80OW<51.05/-40.15>, # Tambovskaya (TB) R2R<52.72/-41.57>,R3R<52.72/-41.57>,R5R<52.72/-41.57>,RA3R<52.72/-41.57>,RA5R<52.72/-41.57>, RC2R<52.72/-41.57>,RC3R<52.72/-41.57>,RC5R<52.72/-41.57>,RD2R<52.72/-41.57>,RD3R<52.72/-41.57>, @@ -5462,8 +5487,8 @@ European Russia: 16: 29: EU: 53.65: -41.37: -4.0: UA: UC4H[30]<53.45/-50.45>,UC4I[30]<53.45/-50.45>,UD4H[30]<53.45/-50.45>,UD4I[30]<53.45/-50.45>, UE4H[30]<53.45/-50.45>,UE4I[30]<53.45/-50.45>,UF4H[30]<53.45/-50.45>,UF4I[30]<53.45/-50.45>, UG4H[30]<53.45/-50.45>,UG4I[30]<53.45/-50.45>,UH4H[30]<53.45/-50.45>,UH4I[30]<53.45/-50.45>, - UI4H[30]<53.45/-50.45>,UI4I[30]<53.45/-50.45>,=R4HAT[29]<53.45/-50.45>,=R4HC[29]<53.45/-50.45>, - =R4HCE[29]<53.45/-50.45>,=R4HCZ[29]<53.45/-50.45>,=R4HD[29]<53.45/-50.45>, + UI4H[30]<53.45/-50.45>,UI4I[30]<53.45/-50.45>,=R2023EN[30]<53.45/-50.45>,=R4HAT[29]<53.45/-50.45>, + =R4HC[29]<53.45/-50.45>,=R4HCE[29]<53.45/-50.45>,=R4HCZ[29]<53.45/-50.45>,=R4HD[29]<53.45/-50.45>, =R4HDC[29]<53.45/-50.45>,=R4HDR[29]<53.45/-50.45>,=R4HL[29]<53.45/-50.45>,=R4IC[29]<53.45/-50.45>, =R4ID[29]<53.45/-50.45>,=R4II[29]<53.45/-50.45>,=R4IK[29]<53.45/-50.45>,=R4IM[29]<53.45/-50.45>, =R4IN[29]<53.45/-50.45>,=R4IO[29]<53.45/-50.45>,=R4IT[29]<53.45/-50.45>,=RA4HL[29]<53.45/-50.45>, @@ -5522,8 +5547,8 @@ European Russia: 16: 29: EU: 53.65: -41.37: -4.0: UA: UD4P<55.55/-50.93>,UD4Q<55.55/-50.93>,UD4R<55.55/-50.93>,UE4P<55.55/-50.93>,UE4Q<55.55/-50.93>, UE4R<55.55/-50.93>,UF4P<55.55/-50.93>,UF4Q<55.55/-50.93>,UF4R<55.55/-50.93>,UG4P<55.55/-50.93>, UG4Q<55.55/-50.93>,UG4R<55.55/-50.93>,UH4P<55.55/-50.93>,UH4Q<55.55/-50.93>,UH4R<55.55/-50.93>, - UI4P<55.55/-50.93>,UI4Q<55.55/-50.93>,UI4R<55.55/-50.93>,=RJ4P[30]<55.55/-50.93>, - =RK4P[30]<55.55/-50.93>,=RM4R[30]<55.55/-50.93>,=UA4PN[30]<55.55/-50.93>, + UI4P<55.55/-50.93>,UI4Q<55.55/-50.93>,UI4R<55.55/-50.93>,=R22SGK<55.55/-50.93>, + =RJ4P[30]<55.55/-50.93>,=RK4P[30]<55.55/-50.93>,=UA4PN[30]<55.55/-50.93>, # Marij-El (MR) R4S<56.70/-47.87>,R4T<56.70/-47.87>,RA4S<56.70/-47.87>,RA4T<56.70/-47.87>,RC4S<56.70/-47.87>, RC4T<56.70/-47.87>,RD4S<56.70/-47.87>,RD4T<56.70/-47.87>,RE4S<56.70/-47.87>,RE4T<56.70/-47.87>, @@ -5537,7 +5562,7 @@ European Russia: 16: 29: EU: 53.65: -41.37: -4.0: UA: UA4T<56.70/-47.87>,UB4S<56.70/-47.87>,UB4T<56.70/-47.87>,UC4S<56.70/-47.87>,UC4T<56.70/-47.87>, UD4S<56.70/-47.87>,UD4T<56.70/-47.87>,UE4S<56.70/-47.87>,UE4T<56.70/-47.87>,UF4S<56.70/-47.87>, UF4T<56.70/-47.87>,UG4S<56.70/-47.87>,UG4T<56.70/-47.87>,UH4S<56.70/-47.87>,UH4T<56.70/-47.87>, - UI4S<56.70/-47.87>,UI4T<56.70/-47.87>, + UI4S<56.70/-47.87>,UI4T<56.70/-47.87>,=UA5B/4<56.70/-47.87>, # Mordoviya (MD) R4U<54.43/-44.45>,RA4U<54.43/-44.45>,RC4U<54.43/-44.45>,RD4U<54.43/-44.45>,RE4U<54.43/-44.45>, RF4U<54.43/-44.45>,RG4U<54.43/-44.45>,RJ4U<54.43/-44.45>,RK4U<54.43/-44.45>,RL4U<54.43/-44.45>, @@ -5620,7 +5645,8 @@ European Russia: 16: 29: EU: 53.65: -41.37: -4.0: UA: UH6D<45.37/-39.43>,UH7A<45.37/-39.43>,UH7B<45.37/-39.43>,UH7C<45.37/-39.43>,UH7D<45.37/-39.43>, UI6A<45.37/-39.43>,UI6B<45.37/-39.43>,UI6C<45.37/-39.43>,UI6D<45.37/-39.43>,UI7A<45.37/-39.43>, UI7B<45.37/-39.43>,UI7C<45.37/-39.43>,UI7D<45.37/-39.43>,=R0SBC/6<45.37/-39.43>, - =R9GM/6<45.37/-39.43>,=RA9ULL/6<45.37/-39.43>,=RA9UUY/6<45.37/-39.43>,=RT9K/6<45.37/-39.43>, + =R9GM/6<45.37/-39.43>,=R9OM/6<45.37/-39.43>,=RA9ULL/6<45.37/-39.43>,=RA9UUY/6<45.37/-39.43>, + =RT9K/6<45.37/-39.43>, # Karachay-Cherkessia (KC) R6E<43.92/-41.78>,R7E<43.92/-41.78>,RA6E<43.92/-41.78>,RA7E<43.92/-41.78>,RC6E<43.92/-41.78>, RC7E<43.92/-41.78>,RD6E<43.92/-41.78>,RD7E<43.92/-41.78>,RE6E<43.92/-41.78>,RE7E<43.92/-41.78>, @@ -5753,7 +5779,7 @@ European Russia: 16: 29: EU: 53.65: -41.37: -4.0: UA: UH6M<47.87/-41.18>,UH6N<47.87/-41.18>,UH7L<47.87/-41.18>,UH7M<47.87/-41.18>,UH7N<47.87/-41.18>, UI6L<47.87/-41.18>,UI6M<47.87/-41.18>,UI6N<47.87/-41.18>,UI7L<47.87/-41.18>,UI7M<47.87/-41.18>, UI7N<47.87/-41.18>,=R2022DX<47.87/-41.18>,=R2023NY<47.87/-41.18>,=UE23NY<47.87/-41.18>, - =UE32DX<47.87/-41.18>,=UE36EUF<47.87/-41.18>, + =UE32DX<47.87/-41.18>, # Chechnya (CN) R6P<43.40/-45.72>,R7P<43.40/-45.72>,RA6P<43.40/-45.72>,RA7P<43.40/-45.72>,RC6P<43.40/-45.72>, RC7P<43.40/-45.72>,RD6P<43.40/-45.72>,RD7P<43.40/-45.72>,RE6P<43.40/-45.72>,RE7P<43.40/-45.72>, @@ -5991,7 +6017,7 @@ European Russia: 16: 29: EU: 53.65: -41.37: -4.0: UA: Kaliningrad: 15: 29: EU: 54.72: -20.52: -3.0: UA2: R2F,R2K,RA2,RC2F,RC2K,RD2F,RD2K,RE2F,RE2K,RF2F,RF2K,RG2F,RG2K,RJ2F,RJ2K,RK2F,RK2K,RL2F,RL2K,RM2F, RM2K,RN2F,RN2K,RO2F,RO2K,RQ2F,RQ2K,RT2F,RT2K,RU2F,RU2K,RV2F,RV2K,RW2F,RW2K,RX2F,RX2K,RY2F,RY2K, - RZ2F,RZ2K,U2F,U2K,UA2,UB2,UC2,UD2,UE2,UF2,UG2,UH2,UI2,=R2MWO,=RL8C/2,=RN1B/2; + RZ2F,RZ2K,U2F,U2K,UA2,UB2,UC2,UD2,UE2,UF2,UG2,UH2,UI2,=R2MWO,=RL8C/2; # ADIF 15 Asiatic Russia: 17: 30: AS: 55.88: -84.08: -7.0: UA9: R0(19),R8,R9,RA0(19),RA8,RA9,RC0(19),RC8,RC9,RD0(19),RD8,RD9,RE0(19),RE8,RE9,RF0(19),RF8,RF9, @@ -5999,7 +6025,7 @@ Asiatic Russia: 17: 30: AS: 55.88: -84.08: -7.0: UA9: RN0(19),RN8,RN9,RO0(19),RO8,RO9,RQ0(19),RQ8,RQ9,RT0(19),RT8,RT9,RU0(19),RU8,RU9,RV0(19),RV8,RV9, RW0(19),RW8,RW9,RX0(19),RX8,RX9,RY0(19),RY8,RY9,RZ0(19),RZ8,RZ9,U0(19),U8,U9,UA0(19),UA8,UA9, UB0(19),UB8,UB9,UC0(19),UC8,UC9,UD0(19),UD8,UD9,UE0(19),UE8,UE9,UF0(19),UF8,UF9,UG0(19),UG8,UG9, - UH0(19),UH8,UH9,UI0(19),UI8,UI9, + UH0(19),UH8,UH9,UI0(19),UI8,UI9,=UA9AR/P(17), # Chelyabinskaya (CB) R8A<54.75/-60.70>~-6.0~,R8B<54.75/-60.70>~-6.0~,R9A<54.75/-60.70>~-6.0~,R9B<54.75/-60.70>~-6.0~, RA8A<54.75/-60.70>~-6.0~,RA8B<54.75/-60.70>~-6.0~,RA9A<54.75/-60.70>~-6.0~, @@ -6146,7 +6172,7 @@ Asiatic Russia: 17: 30: AS: 55.88: -84.08: -7.0: UA9: UG9H(18)[31]<58.75/-82.13>,UG9I(18)[31]<58.75/-82.13>,UH8H(18)[31]<58.75/-82.13>, UH8I(18)[31]<58.75/-82.13>,UH9H(18)[31]<58.75/-82.13>,UH9I(18)[31]<58.75/-82.13>, UI8H(18)[31]<58.75/-82.13>,UI8I(18)[31]<58.75/-82.13>,UI9H(18)[31]<58.75/-82.13>, - UI9I(18)[31]<58.75/-82.13>, + UI9I(18)[31]<58.75/-82.13>,=RAEM(18)[31]<58.75/-82.13>, # Khanty-Mansyisky (HM) R8J[20]<62.25/-70.17>~-6.0~,R9J[20]<62.25/-70.17>~-6.0~,RA8J[20]<62.25/-70.17>~-6.0~, RA9J[20]<62.25/-70.17>~-6.0~,RC8J[20]<62.25/-70.17>~-6.0~,RC9J[20]<62.25/-70.17>~-6.0~, @@ -6168,7 +6194,7 @@ Asiatic Russia: 17: 30: AS: 55.88: -84.08: -7.0: UA9: UD9J[20]<62.25/-70.17>~-6.0~,UE8J[20]<62.25/-70.17>~-6.0~,UE9J[20]<62.25/-70.17>~-6.0~, UF8J[20]<62.25/-70.17>~-6.0~,UF9J[20]<62.25/-70.17>~-6.0~,UG8J[20]<62.25/-70.17>~-6.0~, UG9J[20]<62.25/-70.17>~-6.0~,UH8J[20]<62.25/-70.17>~-6.0~,UH9J[20]<62.25/-70.17>~-6.0~, - UI8J[20]<62.25/-70.17>~-6.0~,UI9J[20]<62.25/-70.17>~-6.0~, + UI8J[20]<62.25/-70.17>~-6.0~,UI9J[20]<62.25/-70.17>~-6.0~,=RK4PA/9[20]<62.25/-70.17>~-6.0~, # Yamalo-Nenetsky (YN) R8K[20]<65.30/-74.03>~-6.0~,R9K[20]<65.30/-74.03>~-6.0~,RA8K[20]<65.30/-74.03>~-6.0~, RA9K[20]<65.30/-74.03>~-6.0~,RC8K[20]<65.30/-74.03>~-6.0~,RC9K[20]<65.30/-74.03>~-6.0~, @@ -6281,8 +6307,8 @@ Asiatic Russia: 17: 30: AS: 55.88: -84.08: -7.0: UA9: UG9O(18)[31]<55.45/-79.55>,UG9P(18)[31]<55.45/-79.55>,UH8O(18)[31]<55.45/-79.55>, UH8P(18)[31]<55.45/-79.55>,UH9O(18)[31]<55.45/-79.55>,UH9P(18)[31]<55.45/-79.55>, UI8O(18)[31]<55.45/-79.55>,UI8P(18)[31]<55.45/-79.55>,UI9O(18)[31]<55.45/-79.55>, - UI9P(18)[31]<55.45/-79.55>,=R2ET/9(18)[31]<55.45/-79.55>,=RC7LE/9(18)[31]<55.45/-79.55>, - =RO9O(18)[31]<55.45/-79.55>,=RR9O(18)[31]<55.45/-79.55>, + UI9P(18)[31]<55.45/-79.55>,=RC7LE/9(18)[31]<55.45/-79.55>,=RO9O(18)[31]<55.45/-79.55>, + =RR9O(18)[31]<55.45/-79.55>, # Kurganskaya (KN) R8Q<55.57/-64.75>~-6.0~,R8R<55.57/-64.75>~-6.0~,R9Q<55.57/-64.75>~-6.0~,R9R<55.57/-64.75>~-6.0~, RA8Q<55.57/-64.75>~-6.0~,RA8R<55.57/-64.75>~-6.0~,RA9Q<55.57/-64.75>~-6.0~, @@ -6707,7 +6733,6 @@ Asiatic Russia: 17: 30: AS: 55.88: -84.08: -7.0: UA9: UH0L(19)[34]<45.33/-134.67>~-11.0~,UH0M(19)[34]<45.33/-134.67>~-11.0~, UH0N(19)[34]<45.33/-134.67>~-11.0~,UI0L(19)[34]<45.33/-134.67>~-11.0~, UI0M(19)[34]<45.33/-134.67>~-11.0~,UI0N(19)[34]<45.33/-134.67>~-11.0~, - =RV3DSA/0(19)[34]<45.33/-134.67>~-11.0~, # Buryatiya (BU) R0O(18)[32]<53.80/-109.33>~-9.0~,RA0O(18)[32]<53.80/-109.33>~-9.0~, RC0O(18)[32]<53.80/-109.33>~-9.0~,RD0O(18)[32]<53.80/-109.33>~-9.0~, @@ -6822,7 +6847,7 @@ Asiatic Russia: 17: 30: AS: 55.88: -84.08: -7.0: UA9: UC0Y(23)[32]<51.78/-94.75>~-8.0~,UD0Y(23)[32]<51.78/-94.75>~-8.0~, UE0Y(23)[32]<51.78/-94.75>~-8.0~,UF0Y(23)[32]<51.78/-94.75>~-8.0~, UG0Y(23)[32]<51.78/-94.75>~-8.0~,UH0Y(23)[32]<51.78/-94.75>~-8.0~, - UI0Y(23)[32]<51.78/-94.75>~-8.0~,=R0WX/P(23)[32]<51.78/-94.75>~-8.0~, + UI0Y(23)[32]<51.78/-94.75>~-8.0~, # Kamchatskaya (KT) R0X(19)[35]<55.00/-159.00>~-12.0~,R0Z(19)[35]<55.00/-159.00>~-12.0~, RA0X(19)[35]<55.00/-159.00>~-12.0~,RA0Z(19)[35]<55.00/-159.00>~-12.0~, @@ -7868,7 +7893,7 @@ Brunei Darussalam: 28: 54: OC: 4.50: -114.60: -8.0: V8: V8; # ADIF 1 Canada: 05: 09: NA: 44.35: 78.75: 5.0: VE: - CF,CG,CJ,CK,VA,VB,VC,VE,VG,VX,VY9,XL,XM,=VE3JZT,=VER20221222, + CF,CG,CJ,CK,VA,VB,VC,VE,VG,VX,VY9,XL,XM,=VER20230106, # Nova Scotia (NS) CF1<45.13/62.48>~4.0~,CG1<45.13/62.48>~4.0~,CJ1<45.13/62.48>~4.0~,CK1<45.13/62.48>~4.0~, VA1<45.13/62.48>~4.0~,VB1<45.13/62.48>~4.0~,VC1<45.13/62.48>~4.0~,VE1<45.13/62.48>~4.0~, @@ -7942,7 +7967,7 @@ Australia: 30: 59: OC: -23.70: -132.33: -10.0: VK: VL2<-34.00/-151.00>, # Victoria (VIC) AX3<-38.00/-145.00>,VI3<-38.00/-145.00>,VJ3<-38.00/-145.00>,VK3<-38.00/-145.00>, - VL3<-38.00/-145.00>, + VL3<-38.00/-145.00>,=VI75G<-38.00/-145.00>, # Queensland (QLD) AX4[55]<-27.50/-153.00>,VI4[55]<-27.50/-153.00>,VJ4[55]<-27.50/-153.00>,VK4[55]<-27.50/-153.00>, VL4[55]<-27.50/-153.00>, @@ -7977,7 +8002,7 @@ Mellish Reef: 30: 56: OC: -17.40: -155.85: -10.0: VK9M: AX9M,VI9M,VK9M,VZ9M; # ADIF 189 Norfolk Island: 32: 60: OC: -29.03: -167.93: -11.5: VK9N: - AX9,VI9,VK9,=VK9MTO; + AX9,VI9,VK9,=VK2NOW/9,=VK9MTO; # ADIF 303 Willis Island: 30: 55: OC: -16.22: -150.02: -10.0: VK9W: AX9W,AX9Z,VI9W,VI9Z,VK9FW,VK9W,VK9Z,VZ9W; @@ -7989,7 +8014,7 @@ Anguilla: 08: 11: NA: 18.23: 63.00: 4.0: VP2E: VP2E; # ADIF 96 Montserrat: 08: 11: NA: 16.75: 62.18: 4.0: VP2M: - VP2M,=VERSION; + VP2M; # ADIF 65 British Virgin Islands: 08: 11: NA: 18.33: 64.75: 4.0: VP2V: VP2V; @@ -8007,7 +8032,7 @@ Falkland Islands: 13: 16: SA: -51.63: 58.72: 4.0: VP8: VP8; # ADIF 235 South Georgia Island: 13: 73: SA: -54.48: 37.08: 2.0: VP8/g: - =VP8CA; + =VP8DXU; # ADIF 241 South Shetland Islands: 13: 73: SA: -62.08: 58.67: 4.0: VP8/h: CE9,XR9,=HF0POL,=LU1ZI; @@ -8016,7 +8041,7 @@ South Orkney Islands: 13: 73: SA: -60.60: 45.55: 3.0: VP8/o: =VP8PJ; # ADIF 240 South Sandwich Islands: 13: 73: SA: -58.43: 26.33: 2.0: VP8/s: - =VP8DXU; + =VP8STI; # ADIF 64 Bermuda: 05: 11: NA: 32.32: 64.73: 4.0: VP9: VP9; diff --git a/static/html/dev/_base.html b/static/html/dev/_base.html new file mode 120000 index 0000000..c83d2f5 --- /dev/null +++ b/static/html/dev/_base.html @@ -0,0 +1 @@ +../../../templates/_base.html \ No newline at end of file diff --git a/static/html/dev/offline.html b/static/html/dev/offline.html new file mode 120000 index 0000000..efae0ad --- /dev/null +++ b/static/html/dev/offline.html @@ -0,0 +1 @@ +../../../templates/offline.html \ No newline at end of file diff --git a/static/html/offline.html b/static/html/offline.html index 06cd29f..c5f1d4d 100644 --- a/static/html/offline.html +++ b/static/html/offline.html @@ -2,11 +2,11 @@ - DX Cluster from IU1BOW: OFFLINE + DX Cluster from IU1BOW: OFFLINE - - + + @@ -16,7 +16,7 @@ - + @@ -24,14 +24,14 @@ - +
- + - - - + + +
-

WEB DX Cluster

-

Spots list

- +

WEB DX Cluster

+

Spots list

+

Telnet access:

For connect your cluster, write to

- + - - +
- - - + + + - + - + - - - + + + + + + diff --git a/static/html/rel/offline.html b/static/html/rel/offline.html new file mode 100644 index 0000000..6a44ecc --- /dev/null +++ b/static/html/rel/offline.html @@ -0,0 +1,136 @@ + + + + + DX Cluster from IU1BOW: OFFLINE + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + +
+
+
+ +

WEB DX Cluster

+

Spots list

+ +

Telnet access:

+

For connect your cluster, write to

+
+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + diff --git a/static/js/README.md b/static/js/README.md deleted file mode 100644 index 6495a30..0000000 --- a/static/js/README.md +++ /dev/null @@ -1 +0,0 @@ -Make changes only on file without '.min.' in their name, then minify using build.sh in 'script' folder diff --git a/static/js/callsign_inline.js b/static/js/callsign_inline.js deleted file mode 100644 index 5e9fbc6..0000000 --- a/static/js/callsign_inline.js +++ /dev/null @@ -1,24 +0,0 @@ -/* - * script loaded inline page in order to prepare data - * for next operations - */ -//var my_adxo_events=jQuery.parseJSON(my_adxo_events_json.replaceAll("\t","")); -var my_adxo_events=JSON.parse(my_adxo_events_json.replaceAll("\t","")); -var rows_list = new Array(); - -var qryString = 'spotlist?c='+my_callsign; -fetch(qryString) -.then((response) => response.json()) -.then((data) => { - try { - //rows_list = buildHtmlTable('bodyspot',data, rows_list, my_callsign); - tb.build(data,my_callsign); - } catch (err) { - console.log(err); - console.log(err.stack); - console.log(data); - } -}) - - - diff --git a/static/js/callsign_inline.min.js b/static/js/callsign_inline.min.js deleted file mode 100644 index 9d0ea18..0000000 --- a/static/js/callsign_inline.min.js +++ /dev/null @@ -1 +0,0 @@ -var my_adxo_events=jQuery.parseJSON(my_adxo_events_json.replaceAll("\t","")),rows_list=[];buildHtmlTable("#bodyspot",payload_json,rows_list,my_callsign); \ No newline at end of file diff --git a/static/js/callsign_search.min.js b/static/js/callsign_search.min.js deleted file mode 100644 index 9d3b188..0000000 --- a/static/js/callsign_search.min.js +++ /dev/null @@ -1 +0,0 @@ -function myCallsignSearch(){callsign=document.getElementById("callsignInput").value;0 (`${new Array(s).fill(0)}${n}`).slice(-s); - var hours = pad(dt_refresh.getHours()); - var minutes = pad(dt_refresh.getMinutes()); - var months_names = get_months_names(); - var month = months_names[dt_refresh.getMonth()]; - var day = dt_refresh.getDate(); - var year = dt_refresh.getFullYear(); - var last_refresh = "Data refresh: " + day + " of " + month + " " + year + " at " + hours + ":" + minutes; - return last_refresh; -} - -/** -* format the data refresh string for exhibit on charts -* -* @returns {Array} list of months as short names -*/ -function get_months_names() { - var months_name = []; - for (let monthNumber = 1; monthNumber < 13; monthNumber++) { - const date = new Date(); - date.setMonth(monthNumber - 1); - months_name.push(date.toLocaleString('en-US', { - month: 'short' - })); - }; - return months_name; -} - -/** -* format the data refresh string for exhibit on charts -* -* @param {Integer} num Number to be formatted -* @returns {string} Number formatted with K for thousands and M for millions -*/ -function format_u_k_m(num) { - let label; - let sign = 1; - if (num < 0) { - sign = -1; - } - let abs_num = Math.abs(num); - if (abs_num == 0) { - label = abs_num - } else { - if (abs_num > 0 && abs_num < 1000) { - label = abs_num * sign; - } else { - if (abs_num >= 1000 && abs_num < 1000000) { - label = abs_num / 1000 * sign + "K"; - } else { - if (abs_num >= 1000000) { - label = abs_num / 1000000 * sign + "M"; - } - } - } - } - return label; -} - -/** -* Set a selected element of a combo box with a value -* -* @param {string} id of the selected -* @param {string} valueToSelect value to assign -*/ -function selectElement(id, valueToSelect) { - let element = document.getElementById(id); - element.value = valueToSelect; -} - -/** -* Add event Handler to element (on) -* -* @param {string} id of the element -* @param {string} eventType i.e. "change" -* @param {function} handler the function to execute whene the evenet is raised -*/ -function addEventHandler(id, eventType, handler) { - if (id.addEventListener) - id.addEventListener(eventType, handler, false); - else if (elem.attachEvent) - id.attachEvent('on' + eventType, handler); -} - -/** -* Set a text to a specific element -* -* @param {string} id of the element -* @param {string} newvalue the value to assign to element -*/ -function setText(id, newvalue) { - var s = document.getElementById(id); - s.innerHTML = newvalue; -} - - -function openModal() { - document.getElementById("backdrop").style.display = "block" - document.getElementById("exampleModal").style.display = "block" - document.getElementById("exampleModal").classList.add("show") -} -function closeModal() { - document.getElementById("backdrop").style.display = "none" - document.getElementById("exampleModal").style.display = "none" - document.getElementById("exampleModal").classList.remove("show") -} - -/* -function doRefresh(){ - -var chartDom = document.getElementById('chart-dx_spots_trend'); -var myChart = echarts.init(chartDom); -plot_dst.refresh(myChart,'/plot_get_dx_spots_trend'); - -}; - -setInterval(function(){doRefresh()}, 5000); - -*/ - diff --git a/static/js/cookie_consent.min.js b/static/js/cookie_consent.min.js deleted file mode 100644 index b13cf02..0000000 --- a/static/js/cookie_consent.min.js +++ /dev/null @@ -1 +0,0 @@ -var fn=function(){document.cookie="cookie_consent=true;SameSite=Strict; Secure;max-age=2592000";$("#cookie-consent-container").modal("hide")};document.getElementById("cookie-consent").onclick=fn;$("#cookie-consent-container").modal({backdrop:"static",keyboard:!1});$("#cookie-consent-container").modal("show"); \ No newline at end of file diff --git a/static/js/.eslintrc.yml b/static/js/dev/.eslintrc.yml similarity index 95% rename from static/js/.eslintrc.yml rename to static/js/dev/.eslintrc.yml index c19a760..ef5dcc3 100644 --- a/static/js/.eslintrc.yml +++ b/static/js/dev/.eslintrc.yml @@ -17,4 +17,4 @@ rules: - single semi: - error - - never + - always diff --git a/static/js/dev/README.md b/static/js/dev/README.md new file mode 100644 index 0000000..e3716ab --- /dev/null +++ b/static/js/dev/README.md @@ -0,0 +1,2 @@ +This folder `static/js/dev/` contains the javascripts module for **development** environment. +You can change these scripts as you want and rebuild the application \ No newline at end of file diff --git a/static/js/dev/callsign_inline.js b/static/js/dev/callsign_inline.js new file mode 100644 index 0000000..a7446a1 --- /dev/null +++ b/static/js/dev/callsign_inline.js @@ -0,0 +1,22 @@ +/* + * script loaded inline page in order to prepare data + * for next operations + */ +//var my_adxo_events=jQuery.parseJSON(my_adxo_events_json.replaceAll("\t","")); +var my_adxo_events = JSON.parse(my_adxo_events_json.replaceAll('\t', '')); + +var qryString = 'spotlist?c=' + my_callsign; +fetch(qryString) + .then((response) => response.json()) + .then((data) => { + try { + tb.build(data, my_callsign); + } catch (err) { + console.log(err); + console.log(err.stack); + console.log(data); + } + }); + + + diff --git a/static/js/callsign_search.js b/static/js/dev/callsign_search.js similarity index 100% rename from static/js/callsign_search.js rename to static/js/dev/callsign_search.js diff --git a/static/js/dev/clock.js b/static/js/dev/clock.js new file mode 100644 index 0000000..4cdccd4 --- /dev/null +++ b/static/js/dev/clock.js @@ -0,0 +1,12 @@ +function showTime(){ + let date=new Date(); + let utc = new Date(date.getTime() + date.getTimezoneOffset() * 60000); + let time = utc.toTimeString().split(' ')[0]; + time = time.split(':')[0]+':'+time.split(':')[1]; + document.getElementById("MyClockDisplay").innerText = time; + document.getElementById("MyClockDisplay").textContent = time; + setTimeout(showTime, 1000); +} + +showTime(); + diff --git a/static/js/dev/common.js b/static/js/dev/common.js new file mode 100644 index 0000000..e52e76f --- /dev/null +++ b/static/js/dev/common.js @@ -0,0 +1,155 @@ +/** + * Create a cookie + * + * @param cname {String} cookie name + * @param cvalue {string} cookie value + * @param exdays {integer} the number of the days for cookie expiration + */ +function setCookie(cname, cvalue, exdays) { + const d = new Date(); + d.setTime(d.getTime() + (exdays * 24 * 60 * 60 * 1000)); + let expires = 'expires=' + d.toUTCString(); + document.cookie = cname + '=' + cvalue + ';' + expires + ';path=/' + ';Samesite=Strict;Secure=True'; +}; + +/** +* get a cookie +* +* @param cname {String} cookie name +* @returns cookie value +*/ +function getCookie(cname) { + let name = cname + '='; + let decodedCookie = decodeURIComponent(document.cookie); + let ca = decodedCookie.split(';'); + for (let i = 0; i < ca.length; i++) { + let c = ca[i]; + while (c.charAt(0) == ' ') { + c = c.substring(1); + } + if (c.indexOf(name) == 0) { + return c.substring(name.length, c.length); + } + } + return ''; +} + + +/** +* format the data refresh string for exhibit on charts +* +* @param date {String} date of the last refresh +* @returns string formatted +*/ +function get_last_refresh(data) { + let dt_refresh = new Date(0); // The 0 there is the key, which sets the date to the epoch + //var dt_refresh; + dt_refresh.setUTCSeconds(data["last_refresh"]); + const pad = (n, s = 2) => (`${new Array(s).fill(0)}${n}`).slice(-s); + const hours = pad(dt_refresh.getHours()); + const minutes = pad(dt_refresh.getMinutes()); + const months_names = get_months_names(); + const month = months_names[dt_refresh.getMonth()]; + const day = dt_refresh.getDate(); + const year = dt_refresh.getFullYear(); + const last_refresh = "Data refresh: " + day + " of " + month + " " + year + " at " + hours + ":" + minutes; + return last_refresh; +} + +/** +* format the data refresh string for exhibit on charts +* +* @returns {Array} list of months as short names +*/ +function get_months_names() { + var months_name = []; + for (let monthNumber = 1; monthNumber < 13; monthNumber++) { + const date = new Date(); + date.setMonth(monthNumber - 1); + months_name.push(date.toLocaleString('en-US', { + month: 'short' + })); + } + return months_name; +} + +/** +* format the data refresh string for exhibit on charts +* +* @param {Integer} num Number to be formatted +* @returns {string} Number formatted with K for thousands and M for millions +*/ +function format_u_k_m(num) { + let label; + let sign = 1; + if (num < 0) { + sign = -1; + } + let abs_num = Math.abs(num); + if (abs_num == 0) { + label = abs_num; + } else { + if (abs_num > 0 && abs_num < 1000) { + label = abs_num * sign; + } else { + if (abs_num >= 1000 && abs_num < 1000000) { + label = abs_num / 1000 * sign + 'K'; + } else { + if (abs_num >= 1000000) { + label = abs_num / 1000000 * sign + 'M'; + } + } + } + } + return label; +} + +/** +* Set a selected element of a combo box with a value +* +* @param {string} id of the selected +* @param {string} valueToSelect value to assign +*/ +function selectElement(id, valueToSelect) { + let element = document.getElementById(id); + element.value = valueToSelect; +} + +/** +* Add event Handler to element (on) +* +* @param {string} id of the element +* @param {string} eventType i.e. "change" +* @param {function} handler the function to execute whene the evenet is raised +*/ +function addEventHandler(id, eventType, handler) { + if (id.addEventListener) + id.addEventListener(eventType, handler, false); + else if (id.attachEvent) + id.attachEvent('on' + eventType, handler); +} + +/** +* Set a text to a specific element +* +* @param {string} id of the element +* @param {string} newvalue the value to assign to element +*/ +function setText(id, newvalue) { + var s = document.getElementById(id); + s.innerHTML = newvalue; +} + +/* +function doRefresh(){ + +var chartDom = document.getElementById('chart-dx_spots_trend'); +var myChart = echarts.init(chartDom); +plot_dst.refresh(myChart,'/plot_get_dx_spots_trend'); + +}; + +setInterval(function(){doRefresh()}, 5000); + +*/ + diff --git a/static/js/cookie_consent.js b/static/js/dev/cookie_consent.js similarity index 57% rename from static/js/cookie_consent.js rename to static/js/dev/cookie_consent.js index c87ba9d..80a68ba 100644 --- a/static/js/cookie_consent.js +++ b/static/js/dev/cookie_consent.js @@ -1,14 +1,14 @@ /* - script used to acquire user conset to cookie banner (and set the cookie consent) + script used to acquire user consent to cookie banner (and set the cookie consent) */ let cookie_modal = new bootstrap.Modal(document.getElementById('cookie_consent_modal'), { - keyboard: false -}) + keyboard: false +}); cookie_modal.show(); //if button is pressed, setting cookie document.getElementById('cookie_consent_btn').onclick = function(){ - setCookie('cookie_consent',true,30); - cookie_modal.hide(); + setCookie('cookie_consent',true,30); + cookie_modal.hide(); }; diff --git a/static/js/copy_date.js b/static/js/dev/copy_date.js similarity index 72% rename from static/js/copy_date.js rename to static/js/dev/copy_date.js index 97bed28..af72919 100644 --- a/static/js/copy_date.js +++ b/static/js/dev/copy_date.js @@ -1 +1 @@ -document.getElementById('copyDate').innerHTML='2020-'.concat(new Date().getFullYear()) +document.getElementById('copyDate').innerHTML='2020-'.concat(new Date().getFullYear()); diff --git a/static/js/index_inline.js b/static/js/dev/index_inline.js similarity index 81% rename from static/js/index_inline.js rename to static/js/dev/index_inline.js index 596e3fb..b9bcd3f 100644 --- a/static/js/index_inline.js +++ b/static/js/dev/index_inline.js @@ -5,8 +5,7 @@ * Moreover, add an event to the button of the filter form */ //var my_adxo_events=jQuery.parseJSON(my_adxo_events_json.replaceAll("\t","")); -var my_adxo_events=JSON.parse(my_adxo_events_json.replaceAll("\t","")); -var rows_list = new Array(); +var my_adxo_events=JSON.parse(my_adxo_events_json.replaceAll('\t','')); refresh_timer(); //run first data fetch var myRefresh = setInterval(refresh_timer, timer_interval_json); diff --git a/static/js/dev/load-sw.js b/static/js/dev/load-sw.js new file mode 100644 index 0000000..887ce07 --- /dev/null +++ b/static/js/dev/load-sw.js @@ -0,0 +1,17 @@ +/* this is used to instance service worker for PWA + * Check compatibility for the browser we're running this in + */ +if ('serviceWorker' in navigator) { + if (navigator.serviceWorker.controller) { + console.log('[PWA Builder] active service worker found, no need to register'); + } else { + // Register the service worker + navigator.serviceWorker + .register('service-worker.js', { + scope: './' + }) + .then(function (reg) { + console.log('[PWA Builder] Service worker has been registered for scope: ' + reg.scope); + }); + } +} diff --git a/static/js/load_css.js b/static/js/dev/load_css.js similarity index 77% rename from static/js/load_css.js rename to static/js/dev/load_css.js index b6ee575..a65a803 100644 --- a/static/js/load_css.js +++ b/static/js/dev/load_css.js @@ -1,6 +1,6 @@ /*! loadCSS. [c]2020 Filament Group, Inc. MIT License */ (function(w){ - "use strict"; + 'use strict'; /* exported loadCSS */ var loadCSS = function( href, before, media, attributes ){ // Arguments explained: @@ -10,13 +10,13 @@ // `media` [OPTIONAL] is the media type or query of the stylesheet. By default it will be 'all' // `attributes` [OPTIONAL] is the Object of attribute name/attribute value pairs to set on the stylesheet's DOM Element. var doc = w.document; - var ss = doc.createElement( "link" ); + var ss = doc.createElement( 'link' ); var ref; if( before ){ ref = before; } else { - var refs = ( doc.body || doc.getElementsByTagName( "head" )[ 0 ] ).childNodes; + var refs = ( doc.body || doc.getElementsByTagName( 'head' )[ 0 ] ).childNodes; ref = refs[ refs.length - 1]; } @@ -29,10 +29,10 @@ } } } - ss.rel = "stylesheet"; + ss.rel = 'stylesheet'; ss.href = href; // temporarily set media to something inapplicable to ensure it'll fetch without blocking render - ss.media = "only x"; + ss.media = 'only x'; // wait until body is defined before injecting link. This ensures a non-blocking load in IE11. function ready( cb ){ @@ -44,8 +44,8 @@ }); } // Inject link - // Note: the ternary preserves the existing behavior of "before" argument, but we could choose to change the argument to "after" in a later release and standardize on ref.nextSibling for all refs - // Note: `insertBefore` is used instead of `appendChild`, for safety re: http://www.paulirish.com/2011/surefire-dom-element-insertion/ + // Note: the ternary preserves the existing behavior of "before" argument, but we could choose to change the argument to "after" in a later release and standardize on ref.nextSibling for all refs + // Note: `insertBefore` is used instead of `appendChild`, for safety re: http://www.paulirish.com/2011/surefire-dom-element-insertion/ ready( function(){ ref.parentNode.insertBefore( ss, ( before ? ref : ref.nextSibling ) ); }); @@ -65,24 +65,24 @@ function loadCB(){ if( ss.addEventListener ){ - ss.removeEventListener( "load", loadCB ); + ss.removeEventListener( 'load', loadCB ); } - ss.media = media || "all"; + ss.media = media || 'all'; } // once loaded, set link's media back to `all` so that the stylesheet applies once it loads if( ss.addEventListener ){ - ss.addEventListener( "load", loadCB); + ss.addEventListener( 'load', loadCB); } ss.onloadcssdefined = onloadcssdefined; onloadcssdefined( loadCB ); return ss; }; // commonjs - if( typeof exports !== "undefined" ){ + if( typeof exports !== 'undefined' ){ exports.loadCSS = loadCSS; } else { w.loadCSS = loadCSS; } -}( typeof global !== "undefined" ? global : this )); +}( typeof global !== 'undefined' ? global : this )); diff --git a/static/js/dev/plot_band_activity.js b/static/js/dev/plot_band_activity.js new file mode 100644 index 0000000..652f6f8 --- /dev/null +++ b/static/js/dev/plot_band_activity.js @@ -0,0 +1,191 @@ +/******************************************************************************** + * javascript used to build band_activity chart + * ******************************************************************************/ +class band_activity { + + /** + * refresh and populate chart + * + * @param {String} my_cart The HTML id where place chart + * @param {string} end_point This is backend end-point for chart datas + * @param {string} region This is the continent name (EU,AF,NA...) of the selected + * @param {Json} bands The frequency band list (160, 80, 60... UHF, SHF) + * @param {Json} continents The continent list( EU, SA, ...) + */ + refresh(my_chart, end_point, region, bands, continents){ + // Asynchronous Data Loading + fetch(end_point+'?continent='+region) + .then((response) => response.json()) + .then((data) => { + // Fill in the data + var last_refresh=get_last_refresh(data); + var dataMap=Array.from(data['band activity']).map(function (item) { + return [item[1], item[0], item[2] || '-']; + }); + //options + my_chart.setOption({ + tooltip: { + position: 'top', + formatter: function (p) { + var format = p.seriesName +' on ' + p.name +' band: '+''+p.data[2]+''; + return format; + } + }, + title: { + text:'Band activity', + subtext: last_refresh, + top: 'top', + left:'left' + }, + toolbox: { + show: true, + showTitle: false, + orient: 'vertical', + right: 'right', + top : 'bottom', + feature: { + mark: { show: true }, + dataView: { show: true, readOnly: true }, + restore: { show: true }, + saveAsImage: { show: true } + } + }, + grid: { + height: '80%', + left: 25, + top: 50, + right: 60, + bottom: 0, + show: true, + backgroundColor: 'rgb(255, 255, 255)', + }, + xAxis: { + type: 'category', + data: bands, + axisTick: { + show: true, + }, + axisLine: { + show: false, + }, + splitArea: { + show: true + } + }, + yAxis: { + type: 'category', + data: continents, + axisTick: { + show: true, + }, + axisLine: { + show: false, + }, + splitArea: { + show: true + } + }, + visualMap: { + calculable: true, + orient: 'vertical', + right: 'right', + top: 'center', + min: 0, + max: 30, + inRange : { + color: ['#ffffe6','yellow','red'] + + } + }, + series: [ + { + name: 'Spots', + type: 'heatmap', + data: dataMap, + label: { + show: false + }, + emphasis: { + itemStyle: { + shadowBlur: 10, + shadowColor: 'rgba(100, 0, 0, 0.5)' + } + } + } + ] + }); + }); + }; + + /** + * Chart creator + * + * @constructor + * @param {String} chart_id The HTML id where place chart + * @param {string} end_point This is backend end-point for chart datas + * @param {Json} cont_cq The continent list( EU, SA, ...) + * @param {Json} band_freq The frequency band list (160, 80, 60... UHF, SHF) + */ + constructor(chart_id, end_point, cont_cq, band_freq) { + // Initialize the echarts instance based on the prepared dom + var chartDom = document.getElementById(chart_id); + var myChart = echarts.init(chartDom); + + //populate continents array + var continents=[]; + cont_cq.forEach(function myFunction(item, index) { + continents[index]=item['id']; + }); + + //populate bands array + var bands=[]; + band_freq.forEach(function myFunction(item, index) { + bands[index]=item['id']; + }); + + //managing region + var selectedContinent=getCookie('user_region'); + var selectedContinent_desc=getCookie('user_region_desc'); + if (!selectedContinent) { + selectedContinent='EU'; + selectedContinent_desc='Europe'; + setCookie('user_region',selectedContinent,60); + setCookie('user_region_desc',selectedContinent_desc,60); + }; + + selectElement('continentInput', selectedContinent); + + addEventHandler(document.getElementById('continentInput'), 'change', function() { + selectedContinent=this.value; + selectedContinent_desc=this.options[this.selectedIndex].text; + setCookie('user_region',selectedContinent,60); + setCookie('user_region_desc',selectedContinent_desc,60); + plot_ba.refresh(myChart, end_point, selectedContinent,bands,continents); + setText('txt_continent','\xa0 Based on DX SPOTS from stations in '+ selectedContinent_desc +' during the last 15 minutes, displayed by Continent and Band'); + }); + + setText('txt_continent','\xa0 Based on DX SPOTS from stations in '+ selectedContinent_desc +' during the last 15 minutes, displayed by Continent and Band'); + + this.refresh(myChart, end_point, selectedContinent,bands,continents); + + /* resize chart*/ + var chart = echarts.init(document.querySelector('#'+chart_id), null); + window.addEventListener('resize',function(){ + chart.resize(); + }); + } +} + +//create object +let plot_ba = new band_activity ('chart-band_activity','/plot_get_heatmap_data',continents_cq,band_frequencies); + +/*setInterval(function(){doRefresh('chart-band_activity','/plot_get_heatmap_data')}, 5000); + +function doRefresh(chart_id,end_point){ + var chartDom = document.getElementById(chart_id); + var myChart = echarts.init(chartDom); + plot_dst.refresh(myChart,end_point); +}; + +*/ + diff --git a/static/js/dev/plot_dx_spots_per_month.js b/static/js/dev/plot_dx_spots_per_month.js new file mode 100644 index 0000000..c316dc9 --- /dev/null +++ b/static/js/dev/plot_dx_spots_per_month.js @@ -0,0 +1,142 @@ +/******************************************************************************** + * javascript used to build dx spots per month chart + * ******************************************************************************/ + +class dx_spots_per_month { + + /** + * refresh and populate chart + * + * @param {String} my_cart The HTML id where place chart + * @param {string} end_point This is backend end-point for chart datas + */ + refresh(my_chart,end_point) { + + // Asynchronous Data Loading + + //$.getJSON(end_point).done(function(data) { + fetch(end_point) + .then((response) => response.json()) + .then((data) => { + // Fill in the data + var last_refresh=get_last_refresh(data); + var year_now = new Date().getFullYear(); + var year_0 = (year_now - 0).toString(); + var year_1 = (year_now - 1).toString(); + var year_2 = (year_now - 2).toString(); + + var months_name = get_months_names(); + + var dataMap_year_0=[]; + var dataMap_year_1=[]; + var dataMap_year_2=[]; + for (let i = 1; i < 13; i++) { + dataMap_year_0.push(data.spots_per_month[i].year_0); + dataMap_year_1.push(data.spots_per_month[i].year_1); + dataMap_year_2.push(data.spots_per_month[i].year_2); + } + + //options + my_chart.setOption({ + tooltip: { + trigger: 'axis', + axisPointer: { + type: 'shadow' + } + }, + title: { + text: 'DX SPOTS per month', + subtext: last_refresh, + top: 'top', + left:'left' + }, + legend: { + data: [year_2, year_1, year_0], + bottom: 'bottom' + }, + toolbox: { + show: true, + showTitle: false, + orient: 'vertical', + left: 'right', + top: 'center', + feature: { + mark: { show: true }, + dataView: { show: true, readOnly: false }, + magicType: { show: true, type: ['line', 'bar', 'stack'] }, + restore: { show: true }, + saveAsImage: { show: true } + } + }, + xAxis: [ + { + type: 'category', + axisTick: { show: false }, + data: months_name + } + ], + yAxis: [ + { + type: 'value', + axisLabel: { + formatter: (function (value){ + return format_u_k_m(value); + }) + } + } + ], + series: [ + { + name: year_2, + type: 'bar', + barGap: 0, + emphasis: { + focus: 'series' + }, + data: dataMap_year_2 + }, + { + name: year_1, + type: 'bar', + emphasis: { + focus: 'series' + }, + data: dataMap_year_1 + + }, + { + name: year_0, + type: 'bar', + emphasis: { + focus: 'series' + }, + data: dataMap_year_0 + }, + ] + }); + }); + } + + /** + * Chart creator + * + * @constructor + * @param {String} chart_id The HTML id where place chart + * @param {string} end_point This is backend end-point for chart datas + */ + constructor(chart_id,end_point) { + // Initialize the echarts instance based on the prepared dom + var chartDom = document.getElementById(chart_id); + var myChart = echarts.init(chartDom); + this.refresh(myChart,end_point); + + //resize + var chart = echarts.init(document.querySelector('#'+chart_id), null); + window.addEventListener('resize',function(){ + chart.resize(); + }) + }; +} + +//create object +let plot_dspm = new dx_spots_per_month ('chart-dx_spots_x_month','/plot_get_dx_spots_per_month'); \ No newline at end of file diff --git a/static/js/dev/plot_dx_spots_trend.js b/static/js/dev/plot_dx_spots_trend.js new file mode 100644 index 0000000..6bd998e --- /dev/null +++ b/static/js/dev/plot_dx_spots_trend.js @@ -0,0 +1,136 @@ +/******************************************************************************** + * javascript used to build dx spots trend + * ******************************************************************************/ + +class dx_spots_trend { + + /** + * refresh and populate chart + * + * @param {String} my_cart The HTML id where place chart + * @param {string} end_point This is backend end-point for chart datas + */ + refresh(my_chart,end_point) { + + // Asynchronous Data Loading + + //$.getJSON(end_point).done(function(data) { + fetch(end_point) + .then((response) => response.json()) + .then((data) => { + // Fill in the data + var last_refresh=get_last_refresh(data); + var dataMap=[]; + for (const [key, value] of Object.entries(data['spots_trend'])) { + var tuple=[]; + tuple.push(key); + tuple.push(value); + dataMap.push(tuple); + } + //options + my_chart.setOption({ + tooltip: { + trigger: 'axis', + position: function (pt) { + return [pt[0], '10%']; + } + }, + title: { + text: 'DX SPOTS trend', + subtext: last_refresh, + top: 'top', + left:'left' + }, + toolbox: { + show: true, + showTitle: false, + orient: 'vertical', + left: 'right', + top: 'center', + feature: { + dataView: { show: true, readOnly: false }, + dataZoom: { + yAxisIndex: 'none' + }, + restore: {}, + magicType: { show: true, type: ['line', 'bar'] }, + saveAsImage: {}, + } + }, + xAxis: { + type: 'time', + boundaryGap: false + }, + yAxis: { + type: 'value', + boundaryGap: [0, '10%'], + axisLabel: { + formatter: (function (value){ + return format_u_k_m(value); + }) + } + }, + dataZoom: [ + { + type: 'inside', + start: 65, + end: 100 + }, + { + start: 0, + end: 20 + }, + ], + + series: [ + { + name: 'Spots', + type: 'line', + smooth: true, + symbol: 'none', + itemStyle: { + color: '#078513' + }, + areaStyle: { + color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [ + { + offset: 0, + color: '#57fa75' + }, + { + offset: 1, + color: '#118226' + } + ]) + }, + data: dataMap + } + ] + + }) + }) + } + + /** + * Chart creator + * + * @constructor + * @param {String} chart_id The HTML id where place chart + * @param {string} end_point This is backend end-point for chart datas + */ + constructor(chart_id,end_point) { + // Initialize the echarts instance based on the prepared dom + var chartDom = document.getElementById(chart_id); + var myChart = echarts.init(chartDom); + this.refresh(myChart,end_point); + + //resize + var chart = echarts.init(document.querySelector('#'+chart_id), null); + window.addEventListener('resize',function(){ + chart.resize(); + }); + } +} + +//create object +let plot_dst = new dx_spots_trend ('chart-dx_spots_trend','/plot_get_dx_spots_trend'); diff --git a/static/js/dev/plot_hour_band.js b/static/js/dev/plot_hour_band.js new file mode 100644 index 0000000..3de942b --- /dev/null +++ b/static/js/dev/plot_hour_band.js @@ -0,0 +1,152 @@ +/******************************************************************************** + * javascript used to build band/hour chart + * ******************************************************************************/ + +class hour_band { + + /** + * refresh and populate chart + * + * @param {String} my_cart The HTML id where place chart + * @param {string} end_point This is backend end-point for chart datas + * @param {Json} bands The frequency band list (160, 80, 60... UHF, SHF) + */ + refresh(my_chart,end_point,bands) { + + // Asynchronous Data Loading + + fetch(end_point) + .then((response) => response.json()) + .then((data) => { + // Fill in the dat + var last_refresh=get_last_refresh(data); + //set hour indicator names + var hour_indicators=[]; + + //for (let i = 0; i < 24; i++) { + for (let i = 23; i > -1; i--) { + var hour_name={}; + var s=i.toString(); + hour_name['name']=s; + hour_indicators.push(hour_name); + } + + //cycling whithin each bands and hours + var dataMap=[]; + bands.forEach(band_item => { + var qso=[]; + + //for (let i = 0; i < 24; i++) { + for (let i = 23; i > -1; i--) { + try { + var value=data['hour_band'][band_item][i]; + if (typeof value == 'undefined') { + value = 0; + } + qso.push(value); + } catch (TypeError) { + //TODO + } + } + var tot={'value':qso,'name':band_item}; + dataMap.push(tot); + }); + + + //options + my_chart.setOption({ + legend: { + + orient: 'horizontal', + left: 'left', + bottom: 'bottom' + }, + title: { + text: 'DX SPOTS per hour in last month', + subtext: last_refresh, + top: 'top', + right: 'right', + }, + tooltip: { + trigger: 'axis', + }, + toolbox: { + show: true, + showTitle: false, + orient: 'vertical', + left: 'right', + top: 'center', + feature: { + mark: { show: true }, + dataView: { show: true, readOnly: true }, + restore: { show: true }, + saveAsImage: { show: true } + } + }, + radar: { + shape: 'circle', + //startAngle: 285, //0 on left side + startAngle: 105, //0 on top + indicator: hour_indicators, + center: ['47%', '46%'], + axisName: { + color: 'rgb(80,80,80)', + }, + }, + series: [ + { + lineStyle: { + width: 2 + }, + type: 'radar', + symbol: 'none', + data: dataMap, + tooltip: { + trigger: 'item', + formatter: (params) => { + return 'Band: '+params.name; + }, + }, + emphasis: { + lineStyle: { + width: 4 + } + }, + } + ] + }) + }) + } + + + /** + * Chart creator + * + * @constructor + * @param {String} chart_id The HTML id where place chart + * @param {string} end_point This is backend end-point for chart datas + * @param {Json} band_freq The frequency band list (160, 80, 60... UHF, SHF) + */ + constructor(chart_id,end_point,band_freq) { + // Initialize the echarts instance based on the prepared dom + var chartDom = document.getElementById(chart_id); + var myChart = echarts.init(chartDom); + + //populate bands array + var bands=[]; + band_freq.forEach(function myFunction(item, index) { + bands[index]=item['id'] + }); + + this.refresh(myChart,end_point,bands); + + //resize + var chart = echarts.init(document.querySelector('#'+chart_id), null); + window.addEventListener('resize',function(){ + chart.resize(); + }); + } +} + +//create object +let plot_hb = new hour_band('chart-hour_band','/plot_get_hour_band',band_frequencies); diff --git a/static/js/dev/plot_world_dx_spots_live.js b/static/js/dev/plot_world_dx_spots_live.js new file mode 100644 index 0000000..30de7a4 --- /dev/null +++ b/static/js/dev/plot_world_dx_spots_live.js @@ -0,0 +1,161 @@ +/******************************************************************************** + * javascript used to build world dx spots live + * ******************************************************************************/ + +class world_dx_spots_live { + + /** + * refresh and populate chart + * + * @param {String} my_cart The HTML id where place chart + * @param {string} end_point This is backend end-point for chart datas + */ + refresh(my_chart,end_point) { + + // Asynchronous Data Loading + fetch(end_point) + .then((response) => response.json()) + .then((data) => { + fetch('world.json') + .then(response => response.text()) + .then(geoJson => { + var last_refresh=get_last_refresh(data); + var dataMap=[]; + data['world_dx_spots_live'].forEach(function myFunction(item, index) { + //lon, lat, number of qso + dataMap.push({'value':[item['lat'],item['lon'],item['count']]}); + }); + + my_chart.hideLoading(); + echarts.registerMap('WR', geoJson); + + my_chart.setOption( { + + visualMap: { + show: false, + min: 0, + max: 50, + inRange: { + symbolSize: [5, 20] + } + }, + + geo: { + type: 'map', + map: 'WR', + roam: true, + zoom: 1.2, + aspectScale: 0.70, + layoutCenter: ['50%', '54%'], + layoutSize: '100%', + itemStyle: { + normal: { + areaColor: '#323c48', + borderColor: '#111' + }, + emphasis: { + areaColor: '#2a333d' + } + }, + label: { + emphasis: { + show: false + } + }, + }, + tooltip: { + trigger: 'item', + formatter: function(val) { + var out='Spots: '+ val.value[2] +''; + return out; + } + + }, + + toolbox: { + show: true, + showTitle: false, + orient: 'vertical', + left: 'right', + top: 'center', + iconStyle: { + borderColor: '#fff', + }, + feature: { + mark: { show: true }, + dataView: { show: true, readOnly: false }, + restore: { show: true }, + saveAsImage: { show: true } + } + }, + legend: { + show: false + }, + //backgroundColor: '#404a59', + backgroundColor: '#596475', + title: { + text: 'World DX SPOTS in last hour', + subtext: last_refresh, + top: 'top', + right:'right', + textStyle: { + color: '#fff' + }, + subtextStyle: { + color: '#fff' + } + }, + series: [ + { + type: 'scatter', + coordinateSystem: 'geo', + data:dataMap, + label: { + emphasis: { + position: 'right', + show: false + } + }, + itemStyle: { + normal: { + color: '#eea638' + } + }, + /* + symbolSize: function (val) { + return val[2] / 4; + }, + */ + } + ] + + }); //end options + + } + ); + }); + } + + /** + * Chart creator + * + * @constructor + * @param {String} chart_id The HTML id where place chart + * @param {string} end_point This is backend end-point for chart datas + */ + constructor(chart_id,end_point) { + // Initialize the echarts instance based on the prepared dom + var chartDom = document.getElementById(chart_id); + var myChart = echarts.init(chartDom); + this.refresh(myChart,end_point); + + //resize + var chart = echarts.init(document.querySelector('#'+chart_id), null); + window.addEventListener('resize',function(){ + chart.resize(); + }); + } +} + +//create object +let plot_wdsl = new world_dx_spots_live ('chart-world_dx_spots_live','/plot_get_world_dx_spots_live'); diff --git a/static/js/dev/table.js b/static/js/dev/table.js new file mode 100644 index 0000000..58e2a05 --- /dev/null +++ b/static/js/dev/table.js @@ -0,0 +1,360 @@ +class table_builder { + /** + * Table builder constructor + * @param selector {string} The html identifier where build the spots table + */ + constructor(selector) { + this.selector = selector; + this.current_data = []; + } + /** + * @return last_rowid {integer} the last rowid + */ + getLastRowId() { + let last_rowid; + if (this.current_data == null) { + last_rowid = 0; + } else { + if (this.current_data.length < 1) { + last_rowid = 0; + } else { + last_rowid = this.current_data[0].rowid; + } + } + return last_rowid; + } + + resetData() { + this.current_data = []; + } + /** + * @param line {object} with the data of a single spot + * @param isnew {boolean} is the new rows indicator + * @param dt_current {string} current date in dd/mm/yyyy format + * @param callsign {string} optional callsign + * @return a <tr> dom element + */ + #buildRow(line, isnew, dt_current, callsign = '') { + const row = document.createElement('tr'); + row.id = line.rowid; + + if (callsign.length > 0) { + if (callsign == line.de) { + row.id = line.rowid; + } else if (callsign == line.dx) { + row.id = line.rowid; + } + } else if (isnew) { + row.className = 'table-info'; + } + + //Column: DE search on QRZ + const i_qrzde = document.createElement('i'); + i_qrzde.className = 'bi-search'; + i_qrzde.role = 'button'; + i_qrzde.ariaLabel = line.de; + const a_qrzde = document.createElement('a'); + a_qrzde.href = qrz_url + line.de; + a_qrzde.target = '_blank'; + a_qrzde.rel = 'noopener'; + const span_qrzde = document.createElement('span'); + + //Mark DE if it found in callsign search + if (line.de == callsign) { + const mark_qrzde = document.createElement('mark'); + mark_qrzde.textContent = line.de; + span_qrzde.appendChild(mark_qrzde); + } else { + span_qrzde.textContent = '\xa0' + line.de; + } + + const td_qrzde = document.createElement('td'); + + a_qrzde.appendChild(i_qrzde); + td_qrzde.appendChild(a_qrzde); + td_qrzde.appendChild(span_qrzde); + row.append(td_qrzde); + + //Column: frequency + var freq = Intl.NumberFormat('it-IT', { + style: 'decimal' + }).format(line.freq); + + const span_freq = document.createElement('span'); + span_freq.className = 'badge bg-warning text-dark badge-responsive'; + span_freq.textContent = freq; + + const td_freq = document.createElement('td'); + td_freq.appendChild(span_freq); + + row.appendChild(td_freq); + + //Column: DX (with ADXO Management) + var adxo = findAdxo(my_adxo_events, line.dx); + var adxo_link = 'NG3K Website'; + const i_qrzdx = document.createElement('i'); + i_qrzdx.className = 'bi-search'; + i_qrzdx.role = 'button'; + i_qrzdx.ariaLabel = line.dx; + const a_qrzdx = document.createElement('a'); + a_qrzdx.href = qrz_url + line.dx; + a_qrzdx.target = '_blank'; + a_qrzdx.rel = 'noopener'; + const span_qrzdx = document.createElement('span'); + + //Mark DX if it found in callsign search + const mark_qrzdx = document.createElement('mark'); + mark_qrzdx.textContent = line.dx; + if (line.dx == callsign) { + span_qrzdx.appendChild(mark_qrzdx); + } else { + span_qrzdx.textContent = '\xa0' + line.dx; + } + + if (adxo != undefined) { + const i_adxo = document.createElement('i'); + i_adxo.tabIndex = 0; + i_adxo.className = 'bi-megaphone-fill'; + i_adxo.style = 'color: cornflowerblue;'; + i_adxo.role = 'button'; + i_adxo.ariaLabel = 'dx_operations'; + i_adxo.setAttribute('data-bs-container', 'body'); + i_adxo.setAttribute('data-bs-toggle', 'popover'); + i_adxo.setAttribute('data-bs-trigger', 'focus'); + i_adxo.setAttribute('data-bs-sanitizer', 'true'); + i_adxo.setAttribute('data-bs-placement', 'auto'); + i_adxo.setAttribute('data-bs-html', 'true'); + i_adxo.setAttribute('data-bs-title', 'Announced DX Op.: ' + adxo.summary); + i_adxo.setAttribute('data-bs-content', adxo.description + 'data from ' + adxo_link); + span_qrzdx.appendChild(i_adxo); + } + + const td_qrzdx = document.createElement('td'); + a_qrzdx.appendChild(i_qrzdx); + td_qrzdx.appendChild(a_qrzdx); + td_qrzdx.append(span_qrzdx); + row.appendChild(td_qrzdx); + + //Column: Flag + try { + const span_flag = document.createElement('span'); + span_flag.className = 'img-flag fi fi-' + line.iso; + span_flag.setAttribute('data-bs-container', 'body'); + span_flag.setAttribute('data-bs-toggle', 'popover'); + span_flag.setAttribute('data-bs-trigger', 'hover'); + span_flag.setAttribute('data-bs-placement', 'left'); + span_flag.setAttribute('data-bs-content', line.country); + + const td_flag = document.createElement('td'); + td_flag.appendChild(span_flag); + row.appendChild(td_flag); + + } catch (err) { + console.log(err); + console.log('error creating flag'); + const td_flag = document.createElement('td'); + row.appendChild(td_flag); + } + + //Column: Country + const td_country_code = document.createElement('td'); + td_country_code.className = 'd-none d-lg-table-cell d-xl-table-cell'; + td_country_code.textContent = line.country; + row.appendChild(td_country_code); + + //Column: Comment + const td_comm = document.createElement('td'); + td_comm.className = 'd-none d-lg-table-cell d-xl-table-cell'; + td_comm.textContent = line.comm; + row.appendChild(td_comm); + + //Column: UTC + let dt = new Date(line.time * 1000); + let hh = '00' + dt.getUTCHours(); + hh = hh.substring(hh.length - 2, hh.length); + let mi = '00' + dt.getMinutes(); + mi = mi.substring(mi.length - 2, mi.length); + let dd = '00' + dt.getUTCDate(); + dd = dd.substring(dd.length - 2, dd.length); + let mo = '00' + (Number(dt.getUTCMonth()) + 1); + mo = mo.substring(mo.length - 2, mo.length); + let yy = dt.getUTCFullYear(); + let tm = hh + ':' + mi; + dt = dd + '/' + mo + '/' + yy; + + const div_date_time = document.createElement('div'); + div_date_time.className = 'd-flex flex-column'; + const p_time = document.createElement('div'); + p_time.textContent = tm; + div_date_time.appendChild(p_time); + if (dt != dt_current) { + const p_date = document.createElement('div'); + p_date.textContent = dt; + div_date_time.appendChild(p_date); + } + + row.appendChild(div_date_time); + + //Finally append the row created to the table + return row; + } + + /** + * Build the table with the spot + * + * @param data {json} The payload with all the spots received from cluster + * @param rl {json} Row List + * @param callsign {string} An optional parameter with the callsign to search + */ + build(data, callsign) { + + if (data != null) { + + //get current date + let d = new Date(); + let dd_current = '00' + d.getUTCDate(); + dd_current = dd_current.substring(dd_current.length - 2, dd_current.length); + let mo_current = '00' + (Number(d.getUTCMonth()) + 1); + mo_current = mo_current.substring(mo_current.length - 2, mo_current.length); + let yy_current = d.getUTCFullYear(); + let dt_current = dd_current + '/' + mo_current + '/' + yy_current; + + //empty the table + document.getElementById(this.selector).replaceChildren(); + + //insert in table new elements + let merge_data = []; + for (let i = 0; i < data.length; i++) { + document.getElementById(this.selector).append(this.#buildRow(data[i], true, dt_current, callsign)); + merge_data.push(data[i]); + } + + //insert in html table previous elements + for (let i = 0; i < this.current_data.length - data.length; i++) { + document.getElementById(this.selector).append(this.#buildRow(this.current_data[i], false, dt_current, callsign)); + merge_data.push(this.current_data[i]); + } + + //replace current data with merge + this.current_data = merge_data; + + var popoverTriggerList = [].slice.call(document.querySelectorAll('[data-bs-toggle="popover"]')); + var popoverList = popoverTriggerList.map(function (popoverTriggerEl) { + return new bootstrap.Popover(popoverTriggerEl); + }); + } + } +} //end class + + +/******************************************************************************** + * javascript used to popolate main table with spots + * ******************************************************************************/ +const adxo_url = 'https://www.ng3k.com/misc/adxo.html'; +const qrz_url = 'https://www.qrz.com/db/'; +const tb = new table_builder('bodyspot'); +var qryAll_sv = ''; + +/** + * Decode Announced Dx Operation (ng3k) + * + * @param adxo {adxo} This is the json containing all the dxo events + * @param callsign_to_find {callsign_to_find} The callsign of the current dx line + */ +function findAdxo(adxo, callsign_to_find) { + if (adxo) { + for (let i = 0; i < adxo.length; i++) { + if (adxo[i].callsign == callsign_to_find) { + return adxo[i]; + } + } + } +} +/** + * Function to filter spot when pressed the search button on filter + * This function trigger the search, also triggered by timer + */ +function mySearch(event) { + event.preventDefault(); + refresh_timer(); //force the call of query +} + +/** + * Function for construct query string for single value selection + * + * @param id {string} The html identifier used for filter + * @param param {string}the parameter for the query + * @param len {number} The maximum number of element that could be selected; use -1 if the filter permits a single selection + * @param qrystr {string} Th initial query string to be completed with the new filter + */ +function getFilter(id, param, len, qrystr) { + + let selectedFilter = [].map.call(document.getElementById(id).selectedOptions, option => option.value); + let qryFilter = ''; + if (selectedFilter.length < len || len == -1) { + qryFilter = selectedFilter.map(function (el) { + if (el) { + return param + '=' + el; + } else { + return ''; + } + }).join('&'); + qrystr = qrystr.concat('&'.concat(qryFilter)); + if (qrystr.substring(0, 1) == '&') { + qrystr = qrystr.substring(1); + } + } + + return qrystr; +} + +/** + * Search / Filter cluster spot based on filter settings + * Gets the filter values, constructs the query parameter and + * make the request to the server + */ +function refresh_timer() { + + let qryAll = ''; + + //get other filters + qryAll = getFilter('band', 'b', 14, qryAll); + qryAll = getFilter('de_re', 'e', 7, qryAll); + qryAll = getFilter('dx_re', 'x', 7, qryAll); + qryAll = getFilter('mode', 'm', 3, qryAll); + qryAll = getFilter('cqdeInput', 'qe', -1, qryAll); + qryAll = getFilter('cqdxInput', 'qx', -1, qryAll); + + //Composing query string + let qryString; + + //If the filter is changed we need to reset the data table and restart from rowid=0 + if (qryAll != qryAll_sv) { + tb.resetData(); + qryAll_sv = qryAll; + } + + qryString = ('spotlist?lr=').concat(tb.getLastRowId()); + + if (qryAll) { + qryString = qryString.concat('&'.concat(qryAll)); + } + + + console.log(qryString); + + //Open a new connection, using the GET request on the URL endpoint + fetch(qryString) + .then((response) => response.json()) + .then((data_new) => { + try { + tb.build(data_new); + } catch (err) { + console.log(err); + console.log(err.stack); + console.log(data_new); + } + }); +} + + diff --git a/static/js/index_inline.min.js b/static/js/index_inline.min.js deleted file mode 100644 index baa526f..0000000 --- a/static/js/index_inline.min.js +++ /dev/null @@ -1 +0,0 @@ -var my_adxo_events=jQuery.parseJSON(my_adxo_events_json.replaceAll("\t","")),rows_list=[];buildHtmlTable("#bodyspot",payload_json,rows_list);var myRefresh=setInterval(myTimer,timer_interval_json);window.onload=function(){document.getElementById("form-filters").addEventListener("submit",mySearch)}; \ No newline at end of file diff --git a/static/js/load-sw.js b/static/js/load-sw.js deleted file mode 100644 index 52b055f..0000000 --- a/static/js/load-sw.js +++ /dev/null @@ -1,17 +0,0 @@ -/* this is used to instance service worker for PWA - * Check compatibility for the browser we're running this in - */ -if ("serviceWorker" in navigator) { - if (navigator.serviceWorker.controller) { - console.log("[PWA Builder] active service worker found, no need to register"); - } else { - // Register the service worker - navigator.serviceWorker - .register("service-worker.js", { - scope: "./" - }) - .then(function (reg) { - console.log("[PWA Builder] Service worker has been registered for scope: " + reg.scope); - }); - } -} diff --git a/static/js/load_css.min.js b/static/js/load_css.min.js deleted file mode 100644 index b2458f7..0000000 --- a/static/js/load_css.min.js +++ /dev/null @@ -1,4 +0,0 @@ -/* - loadCSS. [c]2020 Filament Group, Inc. MIT License */ -(function(l){var e=function(e,f,q,d){function m(a){if(b.body)return a();setTimeout(function(){m(a)})}function g(){a.addEventListener&&a.removeEventListener("load",g);a.media=q||"all"}var b=l.document,a=b.createElement("link");if(f)var c=f;else{var n=(b.body||b.getElementsByTagName("head")[0]).childNodes;c=n[n.length-1]}var p=b.styleSheets;if(d)for(var h in d)d.hasOwnProperty(h)&&a.setAttribute(h,d[h]);a.rel="stylesheet";a.href=e;a.media="only x";m(function(){c.parentNode.insertBefore(a,f?c:c.nextSibling)}); -var k=function(b){for(var d=a.href,c=p.length;c--;)if(p[c].href===d)return b();setTimeout(function(){k(b)})};a.addEventListener&&a.addEventListener("load",g);a.onloadcssdefined=k;k(g);return a};"undefined"!==typeof exports?exports.loadCSS=e:l.loadCSS=e})("undefined"!==typeof global?global:this); \ No newline at end of file diff --git a/static/js/plot_band_activity.js b/static/js/plot_band_activity.js deleted file mode 100644 index ea90242..0000000 --- a/static/js/plot_band_activity.js +++ /dev/null @@ -1,191 +0,0 @@ -/******************************************************************************** - * javascript used to build band_activity chart - * ******************************************************************************/ -class band_activity { - - /** - * refresh and populate chart - * - * @param {String} my_cart The HTML id where place chart - * @param {string} end_point This is backend end-point for chart datas - * @param {string} region This is the continent name (EU,AF,NA...) of the selected - * @param {Json} bands The frequency band list (160, 80, 60... UHF, SHF) - * @param {Json} continents The continent list( EU, SA, ...) - */ - refresh(my_chart, end_point, region, bands, continents){ - // Asynchronous Data Loading - fetch(end_point+'?continent='+region) - .then((response) => response.json()) - .then((data) => { - // Fill in the data - var last_refresh=get_last_refresh(data); - var dataMap=Array.from(data["band activity"]).map(function (item) { - return [item[1], item[0], item[2] || '-']; - }); - //options - my_chart.setOption({ - tooltip: { - position: 'top', - formatter: function (p) { - var format = p.seriesName +' on ' + p.name +' band: '+''+p.data[2]+''; - return format; - } - }, - title: { - text:"Band activity", - subtext: last_refresh, - top: 'top', - left:'left' - }, - toolbox: { - show: true, - showTitle: false, - orient: 'vertical', - right: 'right', - top : 'bottom', - feature: { - mark: { show: true }, - dataView: { show: true, readOnly: true }, - restore: { show: true }, - saveAsImage: { show: true } - } - }, - grid: { - height: '80%', - left: 25, - top: 50, - right: 60, - bottom: 0, - show: true, - backgroundColor: 'rgb(255, 255, 255)', - }, - xAxis: { - type: 'category', - data: bands, - axisTick: { - show: true, - }, - axisLine: { - show: false, - }, - splitArea: { - show: true - } - }, - yAxis: { - type: 'category', - data: continents, - axisTick: { - show: true, - }, - axisLine: { - show: false, - }, - splitArea: { - show: true - } - }, - visualMap: { - calculable: true, - orient: 'vertical', - right: 'right', - top: 'center', - min: 0, - max: 30, - inRange : { - color: ['#ffffe6','yellow','red'] - - } - }, - series: [ - { - name: 'Spots', - type: 'heatmap', - data: dataMap, - label: { - show: false - }, - emphasis: { - itemStyle: { - shadowBlur: 10, - shadowColor: 'rgba(100, 0, 0, 0.5)' - } - } - } - ] - }); - }); - }; - - /** - * Chart creator - * - * @constructor - * @param {String} chart_id The HTML id where place chart - * @param {string} end_point This is backend end-point for chart datas - * @param {Json} cont_cq The continent list( EU, SA, ...) - * @param {Json} band_freq The frequency band list (160, 80, 60... UHF, SHF) - */ - constructor(chart_id, end_point, cont_cq, band_freq) { - // Initialize the echarts instance based on the prepared dom - var chartDom = document.getElementById(chart_id); - var myChart = echarts.init(chartDom); - - //populate continents array - var continents=[]; - cont_cq.forEach(function myFunction(item, index) { - continents[index]=item['id'] - }); - - //populate bands array - var bands=[]; - band_freq.forEach(function myFunction(item, index) { - bands[index]=item['id'] - }); - - //managing region - var selectedContinent=getCookie("user_region"); - var selectedContinent_desc=getCookie("user_region_desc"); - if (!selectedContinent) { - selectedContinent="EU"; - selectedContinent_desc="Europe"; - setCookie("user_region",selectedContinent,60); - setCookie("user_region_desc",selectedContinent_desc,60); - }; - - selectElement('continentInput', selectedContinent); - - addEventHandler(document.getElementById('continentInput'), 'change', function() { - selectedContinent=this.value; - selectedContinent_desc=this.options[this.selectedIndex].text; - setCookie("user_region",selectedContinent,60); - setCookie("user_region_desc",selectedContinent_desc,60); - plot_ba.refresh(myChart, end_point, selectedContinent,bands,continents); - setText('txt_continent','\xa0 Based on DX SPOTS from stations in '+ selectedContinent_desc +' during the last 15 minutes, displayed by Continent and Band'); - }); - - setText('txt_continent','\xa0 Based on DX SPOTS from stations in '+ selectedContinent_desc +' during the last 15 minutes, displayed by Continent and Band'); - - this.refresh(myChart, end_point, selectedContinent,bands,continents); - - /* resize chart*/ - var chart = echarts.init(document.querySelector("#"+chart_id), null); - window.addEventListener('resize',function(){ - chart.resize(); - }) - } -} - -//create object -let plot_ba = new band_activity ('chart-band_activity','/plot_get_heatmap_data',continents_cq,band_frequencies); - -/*setInterval(function(){doRefresh('chart-band_activity','/plot_get_heatmap_data')}, 5000); - -function doRefresh(chart_id,end_point){ - var chartDom = document.getElementById(chart_id); - var myChart = echarts.init(chartDom); - plot_dst.refresh(myChart,end_point); -}; - -*/ - diff --git a/static/js/plot_band_activity.min.js b/static/js/plot_band_activity.min.js deleted file mode 100644 index fab1bb5..0000000 --- a/static/js/plot_band_activity.min.js +++ /dev/null @@ -1,10 +0,0 @@ -var $jscomp=$jscomp||{};$jscomp.scope={};$jscomp.ASSUME_ES5=!1;$jscomp.ASSUME_NO_NATIVE_MAP=!1;$jscomp.ASSUME_NO_NATIVE_SET=!1;$jscomp.SIMPLE_FROUND_POLYFILL=!1;$jscomp.defineProperty=$jscomp.ASSUME_ES5||"function"==typeof Object.defineProperties?Object.defineProperty:function(a,c,b){a!=Array.prototype&&a!=Object.prototype&&(a[c]=b.value)}; -$jscomp.getGlobal=function(a){a=["object"==typeof globalThis&&globalThis,"object"==typeof window&&window,"object"==typeof self&&self,"object"==typeof global&&global,a];for(var c=0;c"+a.data[2]+""}},title:{text:"Band activity",subtext:c,top:"top",left:"left"},toolbox:{show:!0,showTitle:!1,orient:"vertical",right:"right",top:"bottom", -feature:{mark:{show:!0},dataView:{show:!0,readOnly:!0},restore:{show:!0},saveAsImage:{show:!0}}},grid:{height:"80%",left:25,top:50,right:60,bottom:0,show:!0,backgroundColor:"rgb(255, 255, 255)"},xAxis:{type:"category",data:d,axisTick:{show:!0},axisLine:{show:!1},splitArea:{show:!0}},yAxis:{type:"category",data:e,axisTick:{show:!0},axisLine:{show:!1},splitArea:{show:!0}},visualMap:{calculable:!0,orient:"vertical",right:"right",top:"center",min:0,max:30,inRange:{color:["#ffffe6","yellow","red"]}},series:[{name:"Spots", -type:"heatmap",data:b,label:{show:!1},emphasis:{itemStyle:{shadowBlur:10,shadowColor:"rgba(100, 0, 0, 0.5)"}}}]})})};var plot_ba=new band_activity("chart-band_activity","/plot_get_heatmap_data",continents_cq,band_frequencies); \ No newline at end of file diff --git a/static/js/plot_common.js b/static/js/plot_common.js deleted file mode 100644 index b763d6d..0000000 --- a/static/js/plot_common.js +++ /dev/null @@ -1,119 +0,0 @@ - -/** -* Create a cookie -* -* @param {String} cname cookie name -* @param {string} cvalue cookie value -* @param {integer} exdays the number of the days for cookie expiration -*/ -function setCookie(cname, cvalue, exdays) { - const d = new Date(); - d.setTime(d.getTime() + (exdays*24*60*60*1000)); - let expires = "expires="+ d.toUTCString(); - document.cookie = cname + "=" + cvalue + ";" + expires + ";path=/" + ";Samesite=Strict;Secure=True"; -}; - -/** -* get a cookie -* -* @param {String} cname cookie name -* @returns {string} cookie value -*/ -function getCookie(cname) { - let name = cname + "="; - let decodedCookie = decodeURIComponent(document.cookie); - let ca = decodedCookie.split(';'); - for(let i = 0; i (`${new Array(s).fill(0)}${n}`).slice(-s); - var hours = pad (dt_refresh.getHours()); - var minutes = pad(dt_refresh.getMinutes()); - var months_names = get_months_names(); - var month = months_names[dt_refresh.getMonth()]; - var day = dt_refresh.getDate(); - var year = dt_refresh.getFullYear(); - var last_refresh = "Data refresh: "+day+" of "+month+" "+year+" at "+hours+":"+minutes; - return last_refresh; -} - - - -/** -* format the data refresh string for exhibit on charts -* -* @returns {Array} list of months as short names -*/ -function get_months_names() { - var months_name=[]; - for (let monthNumber = 1; monthNumber < 13; monthNumber++) { - const date = new Date(); - date.setMonth(monthNumber - 1); - months_name.push(date.toLocaleString('en-US', { month: 'short' })); - }; - return months_name; -} - -/** -* format the data refresh string for exhibit on charts -* -* @param {Integer} num Number to be formatted -* @returns {string} Number formatted with K for thousands and M for millions -*/ -function format_u_k_m(num) { - let label; - let sign=1; - if (num<0){ - sign=-1; - } - let abs_num=Math.abs(num); - if (abs_num==0) { - label=abs_num - } else { - if (abs_num>0 && abs_num <1000) { - label=abs_num*sign; - } else { - if (abs_num>=1000 && abs_num < 1000000) { - label=abs_num/1000*sign+"K"; - } else { - if (abs_num>=1000000) { - label=abs_num/1000000*sign+"M"; - } - } - } - } - return label; -} - -/* -function doRefresh(){ - - var chartDom = document.getElementById('chart-dx_spots_trend'); - var myChart = echarts.init(chartDom); - plot_dst.refresh(myChart,'/plot_get_dx_spots_trend'); - -}; - -setInterval(function(){doRefresh()}, 5000); - -*/ \ No newline at end of file diff --git a/static/js/plot_common.min.js b/static/js/plot_common.min.js deleted file mode 100644 index e4ab992..0000000 --- a/static/js/plot_common.min.js +++ /dev/null @@ -1,6 +0,0 @@ -var $jscomp=$jscomp||{};$jscomp.scope={};$jscomp.createTemplateTagFirstArg=function(a){return a.raw=a};$jscomp.createTemplateTagFirstArgWithRaw=function(a,b){a.raw=b;return a};$jscomp.ASSUME_ES5=!1;$jscomp.ASSUME_NO_NATIVE_MAP=!1;$jscomp.ASSUME_NO_NATIVE_SET=!1;$jscomp.SIMPLE_FROUND_POLYFILL=!1;$jscomp.defineProperty=$jscomp.ASSUME_ES5||"function"==typeof Object.defineProperties?Object.defineProperty:function(a,b,c){a!=Array.prototype&&a!=Object.prototype&&(a[b]=c.value)}; -$jscomp.getGlobal=function(a){a=["object"==typeof globalThis&&globalThis,"object"==typeof window&&window,"object"==typeof self&&self,"object"==typeof global&&global,a];for(var b=0;bc&&(c=Math.max(0,b+c));if(null==d||d>b)d=b;d=Number(d);0>d&&(d=Math.max(0,b+d));for(c=Number(c||0);cb;b++){var c=new Date;c.setMonth(b-1);a.push(c.toLocaleString("en-US",{month:"short"}))}return a} -function format_u_k_m(a){var b,c=1;0>a&&(c=-1);a=Math.abs(a);0==a?b=a:0a?b=a*c:1E3<=a&&1E6>a?b=a/1E3*c+"K":1E6<=a&&(b=a/1E6*c+"M");return b}; \ No newline at end of file diff --git a/static/js/plot_dx_spots_per_month.js b/static/js/plot_dx_spots_per_month.js deleted file mode 100644 index 363460b..0000000 --- a/static/js/plot_dx_spots_per_month.js +++ /dev/null @@ -1,142 +0,0 @@ -/******************************************************************************** - * javascript used to build dx spots per month chart - * ******************************************************************************/ - -class dx_spots_per_month { - - /** - * refresh and populate chart - * - * @param {String} my_cart The HTML id where place chart - * @param {string} end_point This is backend end-point for chart datas - */ - refresh(my_chart,end_point) { - - // Asynchronous Data Loading - - //$.getJSON(end_point).done(function(data) { - fetch(end_point) - .then((response) => response.json()) - .then((data) => { - // Fill in the data - var last_refresh=get_last_refresh(data); - var year_now = new Date().getFullYear(); - var year_0 = (year_now - 0).toString(); - var year_1 = (year_now - 1).toString(); - var year_2 = (year_now - 2).toString(); - - var months_name = get_months_names(); - - var dataMap_year_0=[]; - var dataMap_year_1=[]; - var dataMap_year_2=[]; - for (let i = 1; i < 13; i++) { - dataMap_year_0.push(data.spots_per_month[i].year_0); - dataMap_year_1.push(data.spots_per_month[i].year_1); - dataMap_year_2.push(data.spots_per_month[i].year_2); - } - - //options - my_chart.setOption({ - tooltip: { - trigger: 'axis', - axisPointer: { - type: 'shadow' - } - }, - title: { - text: "DX SPOTS per month", - subtext: last_refresh, - top: 'top', - left:'left' - }, - legend: { - data: [year_2, year_1, year_0], - bottom: "bottom" - }, - toolbox: { - show: true, - showTitle: false, - orient: 'vertical', - left: 'right', - top: 'center', - feature: { - mark: { show: true }, - dataView: { show: true, readOnly: false }, - magicType: { show: true, type: ['line', 'bar', 'stack'] }, - restore: { show: true }, - saveAsImage: { show: true } - } - }, - xAxis: [ - { - type: 'category', - axisTick: { show: false }, - data: months_name - } - ], - yAxis: [ - { - type: 'value', - axisLabel: { - formatter: (function (value){ - return format_u_k_m(value) - }) - } - } - ], - series: [ - { - name: year_2, - type: 'bar', - barGap: 0, - emphasis: { - focus: 'series' - }, - data: dataMap_year_2 - }, - { - name: year_1, - type: 'bar', - emphasis: { - focus: 'series' - }, - data: dataMap_year_1 - - }, - { - name: year_0, - type: 'bar', - emphasis: { - focus: 'series' - }, - data: dataMap_year_0 - }, - ] - }) - }) - } - - /** - * Chart creator - * - * @constructor - * @param {String} chart_id The HTML id where place chart - * @param {string} end_point This is backend end-point for chart datas - */ - constructor(chart_id,end_point) { - // Initialize the echarts instance based on the prepared dom - var chartDom = document.getElementById(chart_id); - var myChart = echarts.init(chartDom); - this.refresh(myChart,end_point); - - //resize - var chart = echarts.init(document.querySelector("#"+chart_id), null); - window.addEventListener('resize',function(){ - chart.resize(); - }) - }; -} - -//create object -let plot_dspm = new dx_spots_per_month ('chart-dx_spots_x_month','/plot_get_dx_spots_per_month'); \ No newline at end of file diff --git a/static/js/plot_dx_spots_per_month.min.js b/static/js/plot_dx_spots_per_month.min.js deleted file mode 100644 index 8a66df7..0000000 --- a/static/js/plot_dx_spots_per_month.min.js +++ /dev/null @@ -1,4 +0,0 @@ -var dx_spots_per_month=function(e,c){var a=document.getElementById(e);a=echarts.init(a);this.refresh(a,c);var m=echarts.init(document.querySelector("#"+e),null);window.addEventListener("resize",function(){m.resize()})}; -dx_spots_per_month.prototype.refresh=function(e,c){fetch(c).then(function(a){return a.json()}).then(function(a){var c=get_last_refresh(a),b=(new Date).getFullYear(),f=(b-0).toString(),g=(b-1).toString();b=(b-2).toString();for(var n=get_months_names(),h=[],k=[],l=[],d=1;13>d;d++)h.push(a.spots_per_month[d].year_0),k.push(a.spots_per_month[d].year_1),l.push(a.spots_per_month[d].year_2);e.setOption({tooltip:{trigger:"axis",axisPointer:{type:"shadow"}},title:{text:"DX SPOTS per month",subtext:c,top:"top", -left:"left"},legend:{data:[b,g,f],bottom:"bottom"},toolbox:{show:!0,showTitle:!1,orient:"vertical",left:"right",top:"center",feature:{mark:{show:!0},dataView:{show:!0,readOnly:!1},magicType:{show:!0,type:["line","bar","stack"]},restore:{show:!0},saveAsImage:{show:!0}}},xAxis:[{type:"category",axisTick:{show:!1},data:n}],yAxis:[{type:"value",axisLabel:{formatter:function(a){return format_u_k_m(a)}}}],series:[{name:b,type:"bar",barGap:0,emphasis:{focus:"series"},data:l},{name:g,type:"bar",emphasis:{focus:"series"}, -data:k},{name:f,type:"bar",emphasis:{focus:"series"},data:h}]})})};var plot_dspm=new dx_spots_per_month("chart-dx_spots_x_month","/plot_get_dx_spots_per_month"); \ No newline at end of file diff --git a/static/js/plot_dx_spots_trend.js b/static/js/plot_dx_spots_trend.js deleted file mode 100644 index fcd846e..0000000 --- a/static/js/plot_dx_spots_trend.js +++ /dev/null @@ -1,136 +0,0 @@ -/******************************************************************************** - * javascript used to build dx spots trend - * ******************************************************************************/ - -class dx_spots_trend { - - /** - * refresh and populate chart - * - * @param {String} my_cart The HTML id where place chart - * @param {string} end_point This is backend end-point for chart datas - */ - refresh(my_chart,end_point) { - - // Asynchronous Data Loading - - //$.getJSON(end_point).done(function(data) { - fetch(end_point) - .then((response) => response.json()) - .then((data) => { - // Fill in the data - var last_refresh=get_last_refresh(data); - var dataMap=[]; - for (const [key, value] of Object.entries(data["spots_trend"])) { - var tuple=[]; - tuple.push(key); - tuple.push(value); - dataMap.push(tuple); - } - //options - my_chart.setOption({ - tooltip: { - trigger: 'axis', - position: function (pt) { - return [pt[0], '10%']; - } - }, - title: { - text: "DX SPOTS trend", - subtext: last_refresh, - top: 'top', - left:'left' - }, - toolbox: { - show: true, - showTitle: false, - orient: 'vertical', - left: 'right', - top: 'center', - feature: { - dataView: { show: true, readOnly: false }, - dataZoom: { - yAxisIndex: 'none' - }, - restore: {}, - magicType: { show: true, type: ['line', 'bar'] }, - saveAsImage: {}, - } - }, - xAxis: { - type: 'time', - boundaryGap: false - }, - yAxis: { - type: 'value', - boundaryGap: [0, '10%'], - axisLabel: { - formatter: (function (value){ - return format_u_k_m(value) - }) - } - }, - dataZoom: [ - { - type: 'inside', - start: 65, - end: 100 - }, - { - start: 0, - end: 20 - }, - ], - - series: [ - { - name: 'Spots', - type: 'line', - smooth: true, - symbol: 'none', - itemStyle: { - color: '#078513' - }, - areaStyle: { - color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [ - { - offset: 0, - color: '#57fa75' - }, - { - offset: 1, - color: '#118226' - } - ]) - }, - data: dataMap - } - ] - - }) - }) - } - - /** - * Chart creator - * - * @constructor - * @param {String} chart_id The HTML id where place chart - * @param {string} end_point This is backend end-point for chart datas - */ - constructor(chart_id,end_point) { - // Initialize the echarts instance based on the prepared dom - var chartDom = document.getElementById(chart_id); - var myChart = echarts.init(chartDom); - this.refresh(myChart,end_point); - - //resize - var chart = echarts.init(document.querySelector("#"+chart_id), null); - window.addEventListener('resize',function(){ - chart.resize(); - }) - }; -} - -//create object -let plot_dst = new dx_spots_trend ('chart-dx_spots_trend','/plot_get_dx_spots_trend'); diff --git a/static/js/plot_dx_spots_trend.min.js b/static/js/plot_dx_spots_trend.min.js deleted file mode 100644 index 1f6b58e..0000000 --- a/static/js/plot_dx_spots_trend.min.js +++ /dev/null @@ -1,8 +0,0 @@ -var $jscomp=$jscomp||{};$jscomp.scope={};$jscomp.arrayIteratorImpl=function(a){var b=0;return function(){return b response.json()) - .then((data) => { - // Fill in the dat - var last_refresh=get_last_refresh(data); - //set hour indicator names - var hour_indicators=[]; - - //for (let i = 0; i < 24; i++) { - for (let i = 23; i > -1; i--) { - var hour_name={}; - var s=i.toString(); - hour_name["name"]=s; - hour_indicators.push(hour_name); - } - - //cycling whithin each bands and hours - var dataMap=[]; - bands.forEach(band_item => { - var qso=[]; - - //for (let i = 0; i < 24; i++) { - for (let i = 23; i > -1; i--) { - try { - var value=data["hour_band"][band_item][i]; - if (typeof value == 'undefined') { - value = 0; - } - qso.push(value); - } catch (TypeError) { - //TODO - } - } - var tot={"value":qso,"name":band_item}; - dataMap.push(tot); - }); - - - //options - my_chart.setOption({ - legend: { - - orient: 'horizontal', - left: 'left', - bottom: 'bottom' - }, - title: { - text: "DX SPOTS per hour in last month", - subtext: last_refresh, - top: 'top', - right: 'right', - }, - tooltip: { - trigger: "axis", - }, - toolbox: { - show: true, - showTitle: false, - orient: 'vertical', - left: 'right', - top: 'center', - feature: { - mark: { show: true }, - dataView: { show: true, readOnly: true }, - restore: { show: true }, - saveAsImage: { show: true } - } - }, - radar: { - shape: 'circle', - //startAngle: 285, //0 on left side - startAngle: 105, //0 on top - indicator: hour_indicators, - center: ['47%', '46%'], - axisName: { - color: 'rgb(80,80,80)', - }, - }, - series: [ - { - lineStyle: { - width: 2 - }, - type: 'radar', - symbol: 'none', - data: dataMap, - tooltip: { - trigger: 'item', - formatter: (params) => { - return "Band: "+params.name; - }, - }, - emphasis: { - lineStyle: { - width: 4 - } - }, - } - ] - }) - }) - } - - - /** - * Chart creator - * - * @constructor - * @param {String} chart_id The HTML id where place chart - * @param {string} end_point This is backend end-point for chart datas - * @param {Json} band_freq The frequency band list (160, 80, 60... UHF, SHF) - */ - constructor(chart_id,end_point,band_freq) { - // Initialize the echarts instance based on the prepared dom - var chartDom = document.getElementById(chart_id); - var myChart = echarts.init(chartDom); - - //populate bands array - var bands=[]; - band_freq.forEach(function myFunction(item, index) { - bands[index]=item['id'] - }); - - this.refresh(myChart,end_point,bands); - - //resize - var chart = echarts.init(document.querySelector("#"+chart_id), null); - window.addEventListener('resize',function(){ - chart.resize(); - }) - }; -} - -//create object -let plot_hb = new hour_band('chart-hour_band','/plot_get_hour_band',band_frequencies); diff --git a/static/js/plot_hour_band.min.js b/static/js/plot_hour_band.min.js deleted file mode 100644 index 043f217..0000000 --- a/static/js/plot_hour_band.min.js +++ /dev/null @@ -1,4 +0,0 @@ -var hour_band=function(c,a,f){var b=document.getElementById(c);b=echarts.init(b);var d=[];f.forEach(function(b,c){d[c]=b.id});this.refresh(b,a,d);var e=echarts.init(document.querySelector("#"+c),null);window.addEventListener("resize",function(){e.resize()})}; -hour_band.prototype.refresh=function(c,a,f){fetch(a).then(function(b){return b.json()}).then(function(b){for(var d=get_last_refresh(b),e=[],a=23;-1 response.json()) - .then((data) => { - fetch('world.json') - .then(response => response.text()) - .then(geoJson => { - var last_refresh=get_last_refresh(data); - var dataMap=[]; - data["world_dx_spots_live"].forEach(function myFunction(item, index) { - //lon, lat, number of qso - dataMap.push({"value":[item["lat"],item["lon"],item["count"]]}); - }); - - my_chart.hideLoading(); - echarts.registerMap('WR', geoJson); - - my_chart.setOption( { - - visualMap: { - show: false, - min: 0, - max: 50, - inRange: { - symbolSize: [5, 20] - } - }, - - geo: { - type: 'map', - map: 'WR', - roam: true, - zoom: 1.2, - aspectScale: 0.70, - layoutCenter: ['50%', '54%'], - layoutSize: '100%', - itemStyle: { - normal: { - areaColor: '#323c48', - borderColor: '#111' - }, - emphasis: { - areaColor: '#2a333d' - } - }, - label: { - emphasis: { - show: false - } - }, - }, - tooltip: { - trigger: 'item', - formatter: function(val) { - var out="lat: "+val.value[0]+ - " lon: "+val.value[1]+"
"+ - "Spots: "+ val.value[2] +"
"; - return out; - } - - }, - - toolbox: { - show: true, - showTitle: false, - orient: 'vertical', - left: 'right', - top: 'center', - iconStyle: { - borderColor: '#fff', - }, - feature: { - mark: { show: true }, - dataView: { show: true, readOnly: false }, - restore: { show: true }, - saveAsImage: { show: true } - } - }, - legend: { - show: false - }, - //backgroundColor: '#404a59', - backgroundColor: '#596475', - title: { - text: "World DX SPOTS in last hour", - subtext: last_refresh, - top: 'top', - right:'right', - textStyle: { - color: '#fff' - }, - subtextStyle: { - color: '#fff' - } - }, - series: [ - { - type: 'scatter', - coordinateSystem: 'geo', - data:dataMap, - label: { - emphasis: { - position: 'right', - show: false - } - }, - itemStyle: { - normal: { - color: '#eea638' - } - }, - /* - symbolSize: function (val) { - return val[2] / 4; - }, - */ - } - ] - - }) //end options - - } - ) - }) - } - - /** - * Chart creator - * - * @constructor - * @param {String} chart_id The HTML id where place chart - * @param {string} end_point This is backend end-point for chart datas - */ - constructor(chart_id,end_point) { - // Initialize the echarts instance based on the prepared dom - var chartDom = document.getElementById(chart_id); - var myChart = echarts.init(chartDom); - this.refresh(myChart,end_point); - - //resize - var chart = echarts.init(document.querySelector("#"+chart_id), null); - window.addEventListener('resize',function(){ - chart.resize(); - }) - } -} - -//create object -let plot_wdsl = new world_dx_spots_live ('chart-world_dx_spots_live','/plot_get_world_dx_spots_live'); diff --git a/static/js/plot_world_dx_spots_live.min.js b/static/js/plot_world_dx_spots_live.min.js deleted file mode 100644 index d8f66d5..0000000 --- a/static/js/plot_world_dx_spots_live.min.js +++ /dev/null @@ -1,4 +0,0 @@ -var world_dx_spots_live=function(b,c){var a=document.getElementById(b);a=echarts.init(a);this.refresh(a,c);var d=echarts.init(document.querySelector("#"+b),null);window.addEventListener("resize",function(){d.resize()})}; -world_dx_spots_live.prototype.refresh=function(b,c){fetch(c).then(function(a){return a.json()}).then(function(a){fetch("world.json").then(function(a){return a.text()}).then(function(c){var d=get_last_refresh(a),e=[];a.world_dx_spots_live.forEach(function(a,b){e.push({value:[a.lat,a.lon,a.count]})});b.hideLoading();echarts.registerMap("WR",c);b.setOption({visualMap:{show:!1,min:0,max:50,inRange:{symbolSize:[5,20]}},geo:{type:"map",map:"WR",roam:!0,zoom:1.2,aspectScale:.7,layoutCenter:["50%","54%"], -layoutSize:"100%",itemStyle:{normal:{areaColor:"#323c48",borderColor:"#111"},emphasis:{areaColor:"#2a333d"}},label:{emphasis:{show:!1}}},tooltip:{trigger:"item",formatter:function(a){return"lat: "+a.value[0]+" lon: "+a.value[1]+"
Spots: "+a.value[2]+"
"}},toolbox:{show:!0,showTitle:!1,orient:"vertical",left:"right",top:"center",iconStyle:{borderColor:"#fff"},feature:{mark:{show:!0},dataView:{show:!0,readOnly:!1},restore:{show:!0},saveAsImage:{show:!0}}},legend:{show:!1},backgroundColor:"#596475", -title:{text:"World DX SPOTS in last hour",subtext:d,top:"top",right:"right",textStyle:{color:"#fff"},subtextStyle:{color:"#fff"}},series:[{type:"scatter",coordinateSystem:"geo",data:e,label:{emphasis:{position:"right",show:!1}},itemStyle:{normal:{color:"#eea638"}}}]})})})};var plot_wdsl=new world_dx_spots_live("chart-world_dx_spots_live","/plot_get_world_dx_spots_live"); \ No newline at end of file diff --git a/static/js/rel/README.md b/static/js/rel/README.md new file mode 100644 index 0000000..a39c2f4 --- /dev/null +++ b/static/js/rel/README.md @@ -0,0 +1,2 @@ +This folder `static/js/rel/` contains the javascript modules for **release** environment. +You **CANNOT change** these scripts. Make changes only in `static/js/dev/` folder \ No newline at end of file diff --git a/static/js/rel/callsign_inline.min.js b/static/js/rel/callsign_inline.min.js new file mode 100644 index 0000000..8cc827f --- /dev/null +++ b/static/js/rel/callsign_inline.min.js @@ -0,0 +1 @@ +var my_adxo_events=JSON.parse(my_adxo_events_json.replaceAll("\t","")),qryString="spotlist?c="+my_callsign;fetch(qryString).then(l=>l.json()).then(o=>{try{tb.build(o,my_callsign)}catch(l){console.log(l),console.log(l.stack),console.log(o)}}); diff --git a/static/js/rel/callsign_search.min.js b/static/js/rel/callsign_search.min.js new file mode 100644 index 0000000..c19cdeb --- /dev/null +++ b/static/js/rel/callsign_search.min.js @@ -0,0 +1 @@ +function myCallsignSearch(){0<(callsign=document.getElementById("callsignInput").value).replace(/\s/g,"").length&&(location.href="/callsign.html?c=".concat(callsign.trim().toUpperCase()))} diff --git a/static/js/rel/clock.min.js b/static/js/rel/clock.min.js new file mode 100644 index 0000000..5b65d86 --- /dev/null +++ b/static/js/rel/clock.min.js @@ -0,0 +1 @@ +function showTime(){var e=new Date;let t=new Date(e.getTime()+6e4*e.getTimezoneOffset()).toTimeString().split(" ")[0];t=t.split(":")[0]+":"+t.split(":")[1],document.getElementById("MyClockDisplay").innerText=t,document.getElementById("MyClockDisplay").textContent=t,setTimeout(showTime,1e3)}showTime(); diff --git a/static/js/rel/common.min.js b/static/js/rel/common.min.js new file mode 100644 index 0000000..23c372a --- /dev/null +++ b/static/js/rel/common.min.js @@ -0,0 +1 @@ +function setCookie(e,t,n){var r=new Date,n=(r.setTime(r.getTime()+24*n*60*60*1e3),"expires="+r.toUTCString());document.cookie=e+"="+t+";"+n+";path=/;Samesite=Strict;Secure=True"}function getCookie(e){var n=e+"=",r=decodeURIComponent(document.cookie).split(";");for(let t=0;t(""+new Array(t).fill(0)+e).slice(-t)),n=e(t.getHours()),e=e(t.getMinutes()),r=get_months_names()[t.getMonth()];return"Data refresh: "+t.getDate()+" of "+r+" "+t.getFullYear()+" at "+n+":"+e}function get_months_names(){var t=[];for(let e=1;e<13;e++){var n=new Date;n.setMonth(e-1),t.push(n.toLocaleString("en-US",{month:"short"}))}return t}function format_u_k_m(e){let t,n=1;e<0&&(n=-1);e=Math.abs(e);return 0==e?t=e:0{document.getElementById("form-filters").addEventListener("submit",mySearch)}; diff --git a/static/js/load-sw.min.js b/static/js/rel/load-sw.min.js similarity index 60% rename from static/js/load-sw.min.js rename to static/js/rel/load-sw.min.js index bd9c49e..dcdc74e 100644 --- a/static/js/load-sw.min.js +++ b/static/js/rel/load-sw.min.js @@ -1 +1 @@ -"serviceWorker"in navigator&&(navigator.serviceWorker.controller?console.log("[PWA Builder] active service worker found, no need to register"):navigator.serviceWorker.register("service-worker.js",{scope:"./"}).then(function(a){console.log("[PWA Builder] Service worker has been registered for scope: "+a.scope)})); \ No newline at end of file +"serviceWorker"in navigator&&(navigator.serviceWorker.controller?console.log("[PWA Builder] active service worker found, no need to register"):navigator.serviceWorker.register("service-worker.js",{scope:"./"}).then(function(e){console.log("[PWA Builder] Service worker has been registered for scope: "+e.scope)})); diff --git a/static/js/rel/load_css.min.js b/static/js/rel/load_css.min.js new file mode 100644 index 0000000..d90eff3 --- /dev/null +++ b/static/js/rel/load_css.min.js @@ -0,0 +1 @@ +!function(c){"use strict";function e(e,t,n,o){var i,r,d=c.document,a=d.createElement("link"),f=(r=t||(i=(d.body||d.getElementsByTagName("head")[0]).childNodes)[i.length-1],d.styleSheets);if(o)for(var l in o)o.hasOwnProperty(l)&&a.setAttribute(l,o[l]);function s(e){for(var t=a.href,n=f.length;n--;)if(f[n].href===t)return e();setTimeout(function(){s(e)})}function u(){a.addEventListener&&a.removeEventListener("load",u),a.media=n||"all"}return a.rel="stylesheet",a.href=e,a.media="only x",function e(t){if(d.body)return t();setTimeout(function(){e(t)})}(function(){r.parentNode.insertBefore(a,t?r:r.nextSibling)}),a.addEventListener&&a.addEventListener("load",u),a.onloadcssdefined=s,s(u),a}"undefined"!=typeof exports?exports.loadCSS=e:c.loadCSS=e}("undefined"!=typeof global?global:this); diff --git a/static/js/rel/plot_band_activity.min.js b/static/js/rel/plot_band_activity.min.js new file mode 100644 index 0000000..f3ee266 --- /dev/null +++ b/static/js/rel/plot_band_activity.min.js @@ -0,0 +1 @@ +class band_activity{refresh(n,t,e,o,i){fetch(t+"?continent="+e).then(t=>t.json()).then(t=>{var e=get_last_refresh(t),t=Array.from(t["band activity"]).map(function(t){return[t[1],t[0],t[2]||"-"]});n.setOption({tooltip:{position:"top",formatter:function(t){return t.seriesName+" on "+t.name+" band: "+t.data[2]+""}},title:{text:"Band activity",subtext:e,top:"top",left:"left"},toolbox:{show:!0,showTitle:!1,orient:"vertical",right:"right",top:"bottom",feature:{mark:{show:!0},dataView:{show:!0,readOnly:!0},restore:{show:!0},saveAsImage:{show:!0}}},grid:{height:"80%",left:25,top:50,right:60,bottom:0,show:!0,backgroundColor:"rgb(255, 255, 255)"},xAxis:{type:"category",data:o,axisTick:{show:!0},axisLine:{show:!1},splitArea:{show:!0}},yAxis:{type:"category",data:i,axisTick:{show:!0},axisLine:{show:!1},splitArea:{show:!0}},visualMap:{calculable:!0,orient:"vertical",right:"right",top:"center",min:0,max:30,inRange:{color:["#ffffe6","yellow","red"]}},series:[{name:"Spots",type:"heatmap",data:t,label:{show:!1},emphasis:{itemStyle:{shadowBlur:10,shadowColor:"rgba(100, 0, 0, 0.5)"}}}]})})}constructor(t,e,n,o){var i=document.getElementById(t),s=echarts.init(i),a=[],r=(n.forEach(function(t,e){a[e]=t.id}),[]),c=(o.forEach(function(t,e){r[e]=t.id}),getCookie("user_region")),d=getCookie("user_region_desc"),h=(c||(c="EU",d="Europe",setCookie("user_region",c,60),setCookie("user_region_desc",d,60)),selectElement("continentInput",c),addEventHandler(document.getElementById("continentInput"),"change",function(){c=this.value,d=this.options[this.selectedIndex].text,setCookie("user_region",c,60),setCookie("user_region_desc",d,60),plot_ba.refresh(s,e,c,r,a),setText("txt_continent","  Based on DX SPOTS from stations in "+d+" during the last 15 minutes, displayed by Continent and Band")}),setText("txt_continent","  Based on DX SPOTS from stations in "+d+" during the last 15 minutes, displayed by Continent and Band"),this.refresh(s,e,c,r,a),echarts.init(document.querySelector("#"+t),null));window.addEventListener("resize",function(){h.resize()})}}let plot_ba=new band_activity("chart-band_activity","/plot_get_heatmap_data",continents_cq,band_frequencies); diff --git a/static/js/rel/plot_dx_spots_per_month.min.js b/static/js/rel/plot_dx_spots_per_month.min.js new file mode 100644 index 0000000..5567c25 --- /dev/null +++ b/static/js/rel/plot_dx_spots_per_month.min.js @@ -0,0 +1 @@ +class dx_spots_per_month{refresh(h,t){fetch(t).then(t=>t.json()).then(e=>{var t=get_last_refresh(e),s=(new Date).getFullYear(),o=(+s).toString(),r=(s-1).toString(),s=(s-2).toString(),a=get_months_names(),n=[],i=[],p=[];for(let t=1;t<13;t++)n.push(e.spots_per_month[t].year_0),i.push(e.spots_per_month[t].year_1),p.push(e.spots_per_month[t].year_2);h.setOption({tooltip:{trigger:"axis",axisPointer:{type:"shadow"}},title:{text:"DX SPOTS per month",subtext:t,top:"top",left:"left"},legend:{data:[s,r,o],bottom:"bottom"},toolbox:{show:!0,showTitle:!1,orient:"vertical",left:"right",top:"center",feature:{mark:{show:!0},dataView:{show:!0,readOnly:!1},magicType:{show:!0,type:["line","bar","stack"]},restore:{show:!0},saveAsImage:{show:!0}}},xAxis:[{type:"category",axisTick:{show:!1},data:a}],yAxis:[{type:"value",axisLabel:{formatter:function(t){return format_u_k_m(t)}}}],series:[{name:s,type:"bar",barGap:0,emphasis:{focus:"series"},data:p},{name:r,type:"bar",emphasis:{focus:"series"},data:i},{name:o,type:"bar",emphasis:{focus:"series"},data:n}]})})}constructor(t,e){var s=document.getElementById(t),s=echarts.init(s),o=(this.refresh(s,e),echarts.init(document.querySelector("#"+t),null));window.addEventListener("resize",function(){o.resize()})}}let plot_dspm=new dx_spots_per_month("chart-dx_spots_x_month","/plot_get_dx_spots_per_month"); diff --git a/static/js/rel/plot_dx_spots_trend.min.js b/static/js/rel/plot_dx_spots_trend.min.js new file mode 100644 index 0000000..50ed244 --- /dev/null +++ b/static/js/rel/plot_dx_spots_trend.min.js @@ -0,0 +1 @@ +class dx_spots_trend{refresh(a,t){fetch(t).then(t=>t.json()).then(t=>{var e,o,r=get_last_refresh(t),s=[];for([e,o]of Object.entries(t.spots_trend)){var n=[];n.push(e),n.push(o),s.push(n)}a.setOption({tooltip:{trigger:"axis",position:function(t){return[t[0],"10%"]}},title:{text:"DX SPOTS trend",subtext:r,top:"top",left:"left"},toolbox:{show:!0,showTitle:!1,orient:"vertical",left:"right",top:"center",feature:{dataView:{show:!0,readOnly:!1},dataZoom:{yAxisIndex:"none"},restore:{},magicType:{show:!0,type:["line","bar"]},saveAsImage:{}}},xAxis:{type:"time",boundaryGap:!1},yAxis:{type:"value",boundaryGap:[0,"10%"],axisLabel:{formatter:function(t){return format_u_k_m(t)}}},dataZoom:[{type:"inside",start:65,end:100},{start:0,end:20}],series:[{name:"Spots",type:"line",smooth:!0,symbol:"none",itemStyle:{color:"#078513"},areaStyle:{color:new echarts.graphic.LinearGradient(0,0,0,1,[{offset:0,color:"#57fa75"},{offset:1,color:"#118226"}])},data:s}]})})}constructor(t,e){var o=document.getElementById(t),o=echarts.init(o),r=(this.refresh(o,e),echarts.init(document.querySelector("#"+t),null));window.addEventListener("resize",function(){r.resize()})}}let plot_dst=new dx_spots_trend("chart-dx_spots_trend","/plot_get_dx_spots_trend"); diff --git a/static/js/rel/plot_hour_band.min.js b/static/js/rel/plot_hour_band.min.js new file mode 100644 index 0000000..f8e7c70 --- /dev/null +++ b/static/js/rel/plot_hour_band.min.js @@ -0,0 +1 @@ +class hour_band{refresh(i,t,s){fetch(t).then(t=>t.json()).then(a=>{var t=get_last_refresh(a),e=[];for(let t=23;-1{var r=[];for(let t=23;-1"Band: "+t.name},emphasis:{lineStyle:{width:4}}}]})})}constructor(t,e,r){var o=document.getElementById(t),o=echarts.init(o),a=[],n=(r.forEach(function(t,e){a[e]=t.id}),this.refresh(o,e,a),echarts.init(document.querySelector("#"+t),null));window.addEventListener("resize",function(){n.resize()})}}let plot_hb=new hour_band("chart-hour_band","/plot_get_hour_band",band_frequencies); diff --git a/static/js/rel/plot_world_dx_spots_live.min.js b/static/js/rel/plot_world_dx_spots_live.min.js new file mode 100644 index 0000000..d7a3c6d --- /dev/null +++ b/static/js/rel/plot_world_dx_spots_live.min.js @@ -0,0 +1 @@ +class world_dx_spots_live{refresh(s,e){fetch(e).then(e=>e.json()).then(r=>{fetch("world.json").then(e=>e.text()).then(e=>{var t=get_last_refresh(r),o=[];r.world_dx_spots_live.forEach(function(e,t){o.push({value:[e.lat,e.lon,e.count]})}),s.hideLoading(),echarts.registerMap("WR",e),s.setOption({visualMap:{show:!1,min:0,max:50,inRange:{symbolSize:[5,20]}},geo:{type:"map",map:"WR",roam:!0,zoom:1.2,aspectScale:.7,layoutCenter:["50%","54%"],layoutSize:"100%",itemStyle:{normal:{areaColor:"#323c48",borderColor:"#111"},emphasis:{areaColor:"#2a333d"}},label:{emphasis:{show:!1}}},tooltip:{trigger:"item",formatter:function(e){return"Spots: "+e.value[2]+""}},toolbox:{show:!0,showTitle:!1,orient:"vertical",left:"right",top:"center",iconStyle:{borderColor:"#fff"},feature:{mark:{show:!0},dataView:{show:!0,readOnly:!1},restore:{show:!0},saveAsImage:{show:!0}}},legend:{show:!1},backgroundColor:"#596475",title:{text:"World DX SPOTS in last hour",subtext:t,top:"top",right:"right",textStyle:{color:"#fff"},subtextStyle:{color:"#fff"}},series:[{type:"scatter",coordinateSystem:"geo",data:o,label:{emphasis:{position:"right",show:!1}},itemStyle:{normal:{color:"#eea638"}}}]})})})}constructor(e,t){var o=document.getElementById(e),o=echarts.init(o),r=(this.refresh(o,t),echarts.init(document.querySelector("#"+e),null));window.addEventListener("resize",function(){r.resize()})}}let plot_wdsl=new world_dx_spots_live("chart-world_dx_spots_live","/plot_get_world_dx_spots_live"); diff --git a/static/js/rel/table.min.js b/static/js/rel/table.min.js new file mode 100644 index 0000000..09e4115 --- /dev/null +++ b/static/js/rel/table.min.js @@ -0,0 +1 @@ +class table_builder{constructor(e){this.selector=e,this.current_data=[]}getLastRowId(){let e;return e=null==this.current_data||this.current_data.length<1?0:this.current_data[0].rowid}resetData(){this.current_data=[]}#buildRow(e,t,n,a=""){var l=document.createElement("tr"),t=(l.id=e.rowid,0NG3K Website",r=document.createElement("i"),d=(r.className="bi-search",r.role="button",r.ariaLabel=e.dx,document.createElement("a")),c=(d.href=qrz_url+e.dx,d.target="_blank",d.rel="noopener",document.createElement("span")),s=document.createElement("mark"),s=(s.textContent=e.dx,e.dx==a?c.appendChild(s):c.textContent=" "+e.dx,null!=o&&((a=document.createElement("i")).tabIndex=0,a.className="bi-megaphone-fill",a.style="color: cornflowerblue;",a.role="button",a.ariaLabel="dx_operations",a.setAttribute("data-bs-container","body"),a.setAttribute("data-bs-toggle","popover"),a.setAttribute("data-bs-trigger","focus"),a.setAttribute("data-bs-sanitizer","true"),a.setAttribute("data-bs-placement","auto"),a.setAttribute("data-bs-html","true"),a.setAttribute("data-bs-title","Announced DX Op.: "+o.summary),a.setAttribute("data-bs-content",o.description+"data from "+t),c.appendChild(a)),document.createElement("td"));d.appendChild(r),s.appendChild(d),s.append(c),l.appendChild(s);try{var i=document.createElement("span"),u=(i.className="img-flag fi fi-"+e.iso,i.setAttribute("data-bs-container","body"),i.setAttribute("data-bs-toggle","popover"),i.setAttribute("data-bs-trigger","hover"),i.setAttribute("data-bs-placement","left"),i.setAttribute("data-bs-content",e.country),document.createElement("td"));u.appendChild(i),l.appendChild(u)}catch(e){console.log(e),console.log("error creating flag");o=document.createElement("td");l.appendChild(o)}t=document.createElement("td"),t.className="d-none d-lg-table-cell d-xl-table-cell",t.textContent=e.country,l.appendChild(t),a=document.createElement("td");a.className="d-none d-lg-table-cell d-xl-table-cell",a.textContent=e.comm,l.appendChild(a);let m=new Date(1e3*e.time),p="00"+m.getUTCHours(),b=(p=p.substring(p.length-2,p.length),"00"+m.getMinutes()),g=(b=b.substring(b.length-2,b.length),"00"+m.getUTCDate()),h=(g=g.substring(g.length-2,g.length),"00"+(Number(m.getUTCMonth())+1));h=h.substring(h.length-2,h.length);r=m.getUTCFullYear(),d=p+":"+b,m=g+"/"+h+"/"+r,c=document.createElement("div"),c.className="d-flex flex-column",s=document.createElement("div");return s.textContent=d,c.appendChild(s),m!=n&&((i=document.createElement("div")).textContent=m,c.appendChild(i)),l.appendChild(c),l}build(n,a){if(null!=n){var l=new Date;let e="00"+l.getUTCDate(),t=(e=e.substring(e.length-2,e.length),"00"+(Number(l.getUTCMonth())+1));t=t.substring(t.length-2,t.length);var l=l.getUTCFullYear(),r=e+"/"+t+"/"+l,d=(document.getElementById(this.selector).replaceChildren(),[]);for(let e=0;ee.value);return a=(e.lengthe.json()).then(t=>{try{tb.build(t)}catch(e){console.log(e),console.log(e.stack),console.log(t)}})} diff --git a/static/js/table.js b/static/js/table.js deleted file mode 100644 index 765a6b4..0000000 --- a/static/js/table.js +++ /dev/null @@ -1,353 +0,0 @@ -class table_builder { - - /** - * Table builder constructor - * @param selector {string} The html identifier where build the spots table - */ - constructor(selector) { - this.selector=selector; - this.current_data=[]; - } - /** - * @return last_rowid {integer} the last rowid - */ - getLastRowId() { - let last_rowid; - if (this.current_data == null) { - last_rowid=0; - } else { - if (this.current_data.length < 1) { - last_rowid=0; - } else { - last_rowid=this.current_data[0].rowid; - } - } - return last_rowid; - } - - /** - * @param line {object} with the data of a single spot - * @param isnew {boolean} is the new rows indicator - * @param dt_current {string} current date in dd/mm/yyyy format - * @param callsign {string} optional callsign - * @return a <tr> dom element - */ - #buildRow(line, isnew, dt_current,callsign=""){ - const row = document.createElement("tr"); - row.id= line.rowid; - - if (callsign.length>0) { - if (callsign == line.de) { - row.id= line.rowid; - } else if (callsign == line.dx) { - row.id= line.rowid; - } - } else if (isnew) { - row.className="table-info"; - } - - //Column: DE search on QRZ - const i_qrzde = document.createElement("i"); - i_qrzde.className="bi-search"; - i_qrzde.role="button"; - i_qrzde.ariaLabel=line.de; - const a_qrzde = document.createElement("a"); - a_qrzde.href=qrz_url + line.de; - a_qrzde.target="_blank"; - a_qrzde.rel="noopener"; - const span_qrzde = document.createElement("span"); - - //Mark DE if it found in callsign search - if (line.de == callsign) { - const mark_qrzde = document.createElement("mark"); - mark_qrzde.textContent = line.de; - span_qrzde.appendChild(mark_qrzde) - } else { - span_qrzde.textContent='\xa0' + line.de; - } - - const td_qrzde = document.createElement("td"); - - a_qrzde.appendChild(i_qrzde); - td_qrzde.appendChild(a_qrzde); - td_qrzde.appendChild(span_qrzde); - row.append(td_qrzde); - - //Column: frequency - var freq = Intl.NumberFormat('it-IT', { - style: 'decimal' - }).format(line.freq); - - const span_freq = document.createElement("span"); - span_freq.className="badge bg-warning text-dark badge-responsive"; - span_freq.textContent=freq; - - const td_freq = document.createElement("td"); - td_freq.appendChild(span_freq); - - row.appendChild(td_freq); - - //Column: DX (with ADXO Management) - var adxo = findAdxo(my_adxo_events, line.dx); - var adxo_link = 'NG3K Website' - const i_qrzdx = document.createElement("i"); - i_qrzdx.className="bi-search"; - i_qrzdx.role="button"; - i_qrzdx.ariaLabel=line.dx; - const a_qrzdx = document.createElement("a"); - a_qrzdx.href=qrz_url + line.dx; - a_qrzdx.target="_blank"; - a_qrzdx.rel="noopener"; - const span_qrzdx = document.createElement("span"); - - //Mark DX if it found in callsign search - const mark_qrzdx = document.createElement("mark"); - mark_qrzdx.textContent = line.dx; - if (line.dx == callsign) { - span_qrzdx.appendChild(mark_qrzdx) - } else { - span_qrzdx.textContent='\xa0' + line.dx; - } - - if (adxo != undefined) { - const i_adxo = document.createElement("i"); - i_adxo.tabIndex=0; - i_adxo.className="bi-megaphone-fill"; - i_adxo.style="color: cornflowerblue;"; - i_adxo.role="button"; - i_adxo.ariaLabel="dx_operations"; - i_adxo.setAttribute('data-bs-container', "body" ); - i_adxo.setAttribute('data-bs-toggle', "popover" ); - i_adxo.setAttribute('data-bs-trigger', "focus" ); - i_adxo.setAttribute('data-bs-sanitizer', "true"); - i_adxo.setAttribute('data-bs-placement', "auto"); - i_adxo.setAttribute('data-bs-html', "true"); - i_adxo.setAttribute('data-bs-title', "Announced DX Op.: " + adxo.summary); - i_adxo.setAttribute('data-bs-content', adxo.description + "data from " + adxo_link); - span_qrzdx.appendChild(i_adxo); - } - - const td_qrzdx = document.createElement("td"); - a_qrzdx.appendChild(i_qrzdx); - td_qrzdx.appendChild(a_qrzdx); - td_qrzdx.append(span_qrzdx); - row.appendChild(td_qrzdx); - - //Column: Flag - try { - const span_flag=document.createElement("span"); - span_flag.className="img-flag fi fi-" + line.iso; - span_flag.setAttribute('data-bs-container', "body" ); - span_flag.setAttribute('data-bs-toggle', "popover" ); - span_flag.setAttribute('data-bs-trigger', "hover" ); - span_flag.setAttribute('data-bs-placement', "left"); - span_flag.setAttribute('data-bs-content', line.country ); - - const td_flag = document.createElement("td"); - td_flag.appendChild(span_flag); - row.appendChild(td_flag); - - } catch (err) { - console.log(err); - console.log("error creating flag"); - const td_flag = document.createElement("td"); - row.appendChild(td_flag); - } - - //Column: Country - const td_country_code = document.createElement("td"); - td_country_code.className="d-none d-lg-table-cell d-xl-table-cell"; - td_country_code.textContent = line.country; - row.appendChild(td_country_code); - - //Column: Comment - const td_comm = document.createElement("td"); - td_comm.className="d-none d-lg-table-cell d-xl-table-cell"; - td_comm.textContent = line.comm; - row.appendChild(td_comm); - - //Column: UTC - let dt = new Date(line.time * 1000); - let hh = '00' + dt.getUTCHours(); - hh = hh.substring(hh.length - 2, hh.length); - let mi = '00' + dt.getMinutes(); - mi = mi.substring(mi.length - 2, mi.length); - let dd = '00' + dt.getUTCDate(); - dd = dd.substring(dd.length - 2, dd.length); - let mo = '00' + (Number(dt.getUTCMonth()) + 1); - mo = mo.substring(mo.length - 2, mo.length); - let yy = dt.getUTCFullYear(); - let tm = hh + ':' + mi; - dt = dd + '/' + mo + '/' + yy; - - const div_date_time = document.createElement("div"); - div_date_time.className="d-flex flex-column"; - const p_time = document.createElement("div"); - p_time.textContent=tm; - div_date_time.appendChild(p_time); - if (dt != dt_current) { - const p_date = document.createElement("div"); - p_date.textContent=dt; - div_date_time.appendChild(p_date); - } - - row.appendChild(div_date_time); - - //Finally append the row created to the table - return row; - } - - /** - * Build the table with the spot - * - * @param data {json} The payload with all the spots received from cluster - * @param rl {json} Row List - * @param callsign {string} An optional parameter with the callsign to search - */ - build(data, callsign) { - - if (data != null) { - - //get current date - let d = new Date(); - let dd_current = '00' + d.getUTCDate(); - dd_current = dd_current.substring(dd_current.length - 2, dd_current.length); - let mo_current = '00' + (Number(d.getUTCMonth()) + 1); - mo_current = mo_current.substring(mo_current.length - 2, mo_current.length); - let yy_current = d.getUTCFullYear(); - let dt_current = dd_current + '/' + mo_current + '/' + yy_current; - - //empty the table - document.getElementById(this.selector).replaceChildren(); - - //insert in table new elements - let merge_data = []; - for (let i = 0; i < data.length; i++) { - document.getElementById(this.selector).append(this.#buildRow(data[i],true,dt_current,callsign)); - merge_data.push(data[i]); - } - - //insert in html table previous elements - for (let i = 0; i < this.current_data.length-data.length; i++) { - document.getElementById(this.selector).append(this.#buildRow(this.current_data[i],false,dt_current,callsign)); - merge_data.push(this.current_data[i]); - } - - //replace current data with merge - this.current_data=merge_data; - - var popoverTriggerList = [].slice.call(document.querySelectorAll('[data-bs-toggle="popover"]')) - var popoverList = popoverTriggerList.map(function (popoverTriggerEl) { - return new bootstrap.Popover(popoverTriggerEl) - }) - - try { - return; - } catch (err) { - return; - } - } - } -} //end class - - -/******************************************************************************** - * javascript used to popolate main table with spots - * ******************************************************************************/ -const adxo_url = 'https://www.ng3k.com/misc/adxo.html' -const qrz_url = 'https://www.qrz.com/db/' -const tb = new table_builder('bodyspot'); - -/** - * Decode Announced Dx Operation (ng3k) - * - * @param adxo {adxo} This is the json containing all the dxo events - * @param callsign_to_find {callsign_to_find} The callsign of the current dx line - */ -function findAdxo(adxo, callsign_to_find) { - if (adxo) { - for (let i = 0; i < adxo.length; i++) { - if (adxo[i].callsign == callsign_to_find) { - return adxo[i]; - } - } - } -} -/** - * Function to filter spot when pressed the search button on filter - * This function trigger the search, also triggered by timer - */ -function mySearch(event) { - - event.preventDefault(); - refresh_timer(); //force the call of query - -} - -/** - * Function for construct query string for single value selection - * - * @param id {string} The html identifier used for filter - * @param param {string}the parameter for the query - * @param len {number} The maximum number of element that could be selected; use -1 if the filter permits a single selection - * @param qrystr {string} Th initial query string to be completed with the new filter - */ -function getFilter(id, param, len, qrystr) { - - selectedFilter = [].map.call(document.getElementById(id).selectedOptions, option => option.value); - var qryFilter = ''; - if (selectedFilter.length < len || len == -1) { - qryFilter = selectedFilter.map(function(el) { - if (el) { - return param + '=' + el; - } else { - return ''; - } - }).join('&'); - qrystr = qrystr.concat('&'.concat(qryFilter)); - if (qrystr.substring(0, 1) == '&') { - qrystr = qrystr.substring(1) - } - } - - return qrystr; -} - -/** - * Search / Filter cluster spot based on filter settings - * Gets the filter values, constructs the query parameter and - * make the request to the server - */ -function refresh_timer() { - - let qryAll = ''; - - //get other filters - qryAll = getFilter('band', 'b', 14, qryAll); - qryAll = getFilter('de_re', 'e', 7, qryAll); - qryAll = getFilter('dx_re', 'x', 7, qryAll); - qryAll = getFilter('mode', 'm', 3, qryAll); - qryAll = getFilter('cqdeInput', 'qe', -1, qryAll); - qryAll = getFilter('cqdxInput', 'qx', -1, qryAll); - - //Composing query string - let qryString = ('spotlist?lr=').concat(tb.getLastRowId()); - if (qryAll) { - qryString = qryString.concat(qryAll); - } - - console.log(qryString) - //Open a new connection, using the GET request on the URL endpoint - fetch(qryString) - .then((response) => response.json()) - .then((data_new) => { - try { - tb.build(data_new); - } catch (err) { - console.log(err); - console.log(err.stack); - console.log(data_new); - } - }) - -} diff --git a/static/js/table.min.js b/static/js/table.min.js deleted file mode 100644 index 0df6c56..0000000 --- a/static/js/table.min.js +++ /dev/null @@ -1,13 +0,0 @@ -var $jscomp=$jscomp||{};$jscomp.scope={};$jscomp.findInternal=function(a,b,c){a instanceof String&&(a=String(a));for(var d=a.length,k=0;k');h=c.find(function(a){return a==b[e].rowid});void 0!=d?d==b[e].de?f=$(''):d==b[e].dx&&(f=$('')):void 0==h&&0'));de=b[e].de==d?""+b[e].de+"":b[e].de;f.append($("").html(' '+de+""));h=Intl.NumberFormat("it-IT",{style:"decimal"}).format(b[e].freq);f.append($("").html(''+h+""));dx=b[e].dx==d?""+b[e].dx+"":b[e].dx; -h=findAdxo(my_adxo_events,b[e].dx);g="NG3K Website";void 0!=h&&(dx=dx+' ');f.append($("").html(' '+dx+""));try{f.append($("").html(''))}catch(p){f.append($(""))}f.append($('').html(b[e].country));f.append($('').html(b[e].comm)); -var l=new Date(1E3*b[e].time);h="00"+l.getUTCHours();h=h.substring(h.length-2,h.length);g="00"+l.getMinutes();g=g.substring(g.length-2,g.length);var m="00"+l.getUTCDate();m=m.substring(m.length-2,m.length);var n="00"+(Number(l.getUTCMonth())+1);n=n.substring(n.length-2,n.length);l=l.getUTCFullYear();tm=h+":"+g;l=m+"/"+n+"/"+l;l==dt_current?f.append($("").html(tm)):f.append($("").html('
'+tm+"
"+ -l+"
"));$(a).append(f)}$(function(){$('[data-bs-toggle="popover"]').popover({container:a})});try{return Array.from(k)}catch(p){}}}function mySearch(a){a.preventDefault();myTimer()} -function getFilter(a,b,c,d){selectedFilter=[].map.call(document.getElementById(a).selectedOptions,function(a){return a.value});a="";if(selectedFilter.length