-->September 6th, 2025<--
Thank you to GolfCharlie and Claude Code for fixing the code to work with the newly changed FAA API! They have saved a lot of time. A new image has been created and will be uploaded today and available later tonight. If you don't want or need the image, you can visit https://github.com/markyharris/livesectional and incorporate the changes into your board now.
--->September 5th, 2025<--
There has been another change to the FAA API which has again broke the map. We are currently working on this. Check back for fix. If you have the fix please post it. - Mark
Hello,
I am working on an 1850 pixel map. But the API request for all of those is too big and the aviationweather.gov server returns an error.
So I modified the code so it will only send a certain amount (300 at the time). It looks like it works but some airports are shown as Undefinded.
Maybe someone can help with this?
Here is the code I changed in the webapp.py
I added this lib at #60
from itertools import islice
This var at #139
max_api_airports = 300 # The max amount of airports from api with one request
I also changed this value LED_COUNT = 2000
And these are my edits from #1533 to #1702
# routine to capture airport information and pass along to web pages.
def get_led_map_info():
logger.debug('In get_led_map_info Routine')
global led_map_url
global led_map_dict
global lat_list
global lon_list
global max_lat
global min_lat
global max_lon
global min_lon
airports_count = len(airports)
lmu_tmp = led_map_url
print ("Number of airports in the list: ", airports_count)
tmp_ap = airports_count
tmp_start = 0
tmp_end = max_api_airports
while (tmp_ap >= 0):
print ("tmp_start: ", tmp_start)
print ("tmp_ap: ", tmp_ap)
print ("tmp_end: ", tmp_end)
for airportcode in islice(airports, tmp_start, tmp_end):
lmu_tmp = lmu_tmp + airportcode + ","
#led_map_url = led_map_url[:-1]
logger.debug(lmu_tmp) # debug url if neccessary
while True: # check internet availability and retry if necessary. If house power outage, map may boot quicker than router.
try:
content = urllib.request.urlopen(lmu_tmp).read()
logger.info('Internet Available')
logger.info(lmu_tmp)
break
except:
logger.warning('FAA Data Not Available')
logger.warning(lmu_tmp)
time.sleep(delay_time)
content = ''
pass
if content == '': # if FAA data not available bypass getting apinfo
return
root = ET.fromstring(content) # Process XML data returned from FAA
for led_map_info in root.iter('METAR'):
stationId = led_map_info.find('station_id').text
try:
lat = led_map_info.find('latitude').text
lon = led_map_info.find('longitude').text
except:
lat = '0'
lon = '0'
lat_list.append(lat)
lon_list.append(lon)
if led_map_info.find('flight_category') is None:
fl_cat = 'Not Reported'
else:
fl_cat = led_map_info.find('flight_category').text
led_map_dict[stationId] = [lat,lon,fl_cat]
tmp_ap = tmp_ap - max_api_airports
tmp_start = tmp_start + max_api_airports
tmp_end = tmp_end + max_api_airports
lmu_tmp = led_map_url
max_lat = max(lat_list)
min_lat = min(lat_list)
max_lon = max(lon_list)
min_lon = min(lon_list)
# routine to capture airport information and pass along to web pages.
def get_apinfo():
logger.debug('In Get_Apinfo Routine')
global orig_apurl
global apinfo_dict
#print (max_api_airports)
airports_count = len(airports)
print ("Number of airports in the list: ", airports_count)
apurl = orig_apurl # Assign base FAA url to temp variable
tmp_ap = airports_count
tmp_start = 0
tmp_end = max_api_airports
while (tmp_ap >= 0):
print ("tmp_start: ", tmp_start)
print ("tmp_ap: ", tmp_ap)
print ("tmp_end: ", tmp_end)
for airportcode in islice(airports, tmp_start, tmp_end):
apurl = apurl + airportcode + ","
#apurl = apurl[:-1]
print ("URL string: ", apurl)
while True: # check internet availability and retry if necessary. If house power outage, map may boot quicker than router.
try:
# s.connect(("8.8.8.8", 80))
content = urllib.request.urlopen(apurl).read()
logger.info('Internet Available')
logger.info(apurl)
break
except:
logger.warning('FAA Data Not Available')
logger.warning(apurl)
time.sleep(delay_time)
content = ''
pass
if content == '': # if FAA data not available bypass getting apinfo
return
root = ET.fromstring(content) # Process XML data returned from FAA
for apinfo in root.iter('Station'):
stationId = apinfo.find('station_id').text
if stationId[0] != 'K':
site = apinfo.find('site').text
country = apinfo.find('country').text
apinfo_dict[stationId] = [site,country]
else:
site = apinfo.find('site').text
state = apinfo.find('state').text
apinfo_dict[stationId] = [site,state]
tmp_ap = tmp_ap - max_api_airports
tmp_start = tmp_start + max_api_airports
tmp_end = tmp_end + max_api_airports
apurl = orig_apurl
#print (content)
#content2 = content.decode()
#file = open("temp123.xml", "w")
#file.write(content2)
#file.close()
"""
root = ET.fromstring(content) # Process XML data returned from FAA
for apinfo in root.iter('Station'):
stationId = apinfo.find('station_id').text
if stationId[0] != 'K':
site = apinfo.find('site').text
country = apinfo.find('country').text
apinfo_dict[stationId] = [site,country]
else:
site = apinfo.find('site').text
state = apinfo.find('state').text
apinfo_dict[stationId] = [site,state]
"""
Marty, all I can say is "Wow". This will be an awesome map. I can't wait to see a picture.
If I understand your question properly, the undefined nature of an airport (as listed in the airport editor) does not necessarily correlate to an error, or missing metar from that airport.
There are a number of weather data products at work on this project. The Airport editor uses an API from weather.gov while the LED's uses XML data returned from https://www.aviationweather.gov/dataserver. For some reason, there are often inconsistencies between these 2 weather products that I can't explain.
So as long as you know that each airport you are using publishes a METAR your LED's should perform properly. This URL will list all the airports and will tell you if it reports a METAR or not;
https://www.aviationweather.gov/docs/metar/stations.txt
Mark
Hi Mark,
thanks for your fast reply. So the API URL used in both functions is just for the LEDs.
They both start with: www.aviationweather.gov
This is how it looks like on the webapp:
When I look up those airports directly on the aviationweather.gov, all of those UNDF ones reporting METARs
Thanks,
Daniel
Yes, that's what has confounded me all this time.
Try this in your browser;
https://api.weather.gov/stations/KPZZ/observations/latest
Then try this;
https://api.weather.gov/stations/KGHB/observations/latest
I have no idea why there is a discrepancy. I've often wondered if I should remove the weather report from the airports editor for this very reason. BTW: The reason for using this api, is that I can use Jquery and Ajax to update the html on the page on the fly.
Hope this helps explain it. - Mark
@markyharris Thanks for the links. I also have an issue when I click on map layout.
I get this error,
builtins.KeyError KeyError: 'KXER' Traceback (most recent call last) File "/usr/lib/python3/dist-packages/flask/app.py", line 2309, in __call__ return self.wsgi_app(environ, start_response) File "/usr/lib/python3/dist-packages/flask/app.py", line 2295, in wsgi_app response = self.handle_exception(e) File "/usr/lib/python3/dist-packages/flask/app.py", line 1741, in handle_exception reraise(exc_type, exc_value, tb) File "/usr/lib/python3/dist-packages/flask/_compat.py", line 35, in reraise raise value File "/usr/lib/python3/dist-packages/flask/app.py", line 2292, in wsgi_app response = self.full_dispatch_request() File "/usr/lib/python3/dist-packages/flask/app.py", line 1815, in full_dispatch_request rv = self.handle_user_exception(e) File "/usr/lib/python3/dist-packages/flask/app.py", line 1718, in handle_user_exception reraise(exc_type, exc_value, tb) File "/usr/lib/python3/dist-packages/flask/_compat.py", line 35, in reraise raise value File "/usr/lib/python3/dist-packages/flask/app.py", line 1813, in full_dispatch_request rv = self.dispatch_request() File "/usr/lib/python3/dist-packages/flask/app.py", line 1799, in dispatch_request return self.view_functions[rule.endpoint](**req.view_args) File "/NeoSectional/webapp.py", line 359, in led_map +"</a><br>Pin Number = "+str(pin_num)+"<br><b><font size=+2 color="+color+">"+led_map_dict[led_ap][2]+"</font></b>" KeyError: 'KXER' The debugger caught an exception in your WSGI application. You can now look at the traceback which led to the error. To switch between the interactive traceback and the plaintext one, you can click on the "Traceback" headline. From the text traceback you can also create a paste of it. For code execution mouse-over the frame you want to debug and click on the console icon on the right side.
KXER is not a recognized airport ID. Could it be mis-spelled?
Try this search on FAA's lookup site;
https://nfdc.faa.gov/nfdcApps/services/ajv5/airportDisplay.jsp?airportId=KXER
When you put a recognizable airport id in you won't get this error. - Mark
You are right. Here's the URL that the LED's use to display the proper flight category;
https://www.aviationweather.gov/adds/dataserver_current/httpparam?dataSource=metars&requestType=retrieve&format=xml&mostRecentForEachStation=constraint&hoursBeforeNow=2.5&stationString=KXER
And it does in fact show the METAR data for KXER. It's a shame that all the weather products could not be synced up. - Mark


