01234567890123456789012345678901234567890123456789012345678901234567890123456789
102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145 243244245246247248249250251252253254255256257258259260261262263264265266 267268269270271272273274275276277278279280281282283284285286 437143724373437443754376437743784379438043814382438343844385438643874388438943904391 43924393439443954396439743984399440044014402440344044405440644074408440944104411441244134414441544164417441844194420442144224423 442444254426442744284429443044314432443344344435443644374438443944404441444244434444444544464447 4448444944504451445244534454445544564457445844594460446144624463446444654466446744684469 472547264727472847294730473147324733473447354736473747384739474047414742474347444745 474647474748 47494750475147524753475447554756475747584759476047614762476347644765476647674768 |
<----SKIPPED LINES---->
# This web-exposed file is used for non-error messages that might highlight data or
# code logic to check into. It is only cleared out manually.
LOGFILE = 'log.txt'
# Identical to the LOGFILE, except it includes just the most recent n lines. Newest
# lines are at the end.
ROLLING_LOGFILE = 'rolling_log.txt' #file for error messages
ROLLING_LOG_SIZE = 1000 # default number of lines which may be overridden by settings file
# Users can trigger .png histograms analogous to the text ones from the web interface;
# this is the folder (within WEBSERVER_PATH) where those files are placed
WEBSERVER_IMAGE_FOLDER = 'images/'
# Multiple histograms can be generated, i.e. for airline, aircraft, day of week, etc.
# The output files are named by the prefix & suffix, i.e.: prefix + type + . + suffix,
# as in histogram_aircraft.png. These names match up to the names expected by the html
# page that displays the images. Also, note that the suffix is interpreted by matplotlib
# to identify the image format to create.
HISTOGRAM_IMAGE_PREFIX = 'histogram_'
HISTOGRAM_IMAGE_SUFFIX = 'png'
# For those of the approximately ten different types of histograms _not_ generated,
# an empty image is copied into the location expected by the webpage instead; this is
# the location of that "empty" image file.
HISTOGRAM_EMPTY_IMAGE_FILE = 'empty.png'
# This file indicates a pending request for histograms - either png, text-based, or
# both; once it is processed, this file is deleted. The contents are concatenated key-value
# pairs, histogram=all;histogram_history=24h; etc.
HISTOGRAM_CONFIG_FILE = 'secure/histogram.txt'
HISTOGRAM_BOOLEANS = ('histogram_data_summary')
# This contains concatenated key-value configuration attributes in a similar format
# to the HISTOGRAM_CONFIG_FILE that are exposed to the user via the web interface or,
# for a subset of them, through the Arduino interface. They are polled at every iteration
# so that the most current value is always leveraged by the running software.
CONFIG_FILE = 'secure/settings.txt'
CONFIG_BOOLEANS = ('setting_screen_enabled', 'next_flight', 'reset_logs', 'log_jsons')
# A few key settings for the messageboard are its sensitivity to displaying flights -
# though it logs all flights within range, it may not be desirable to display all
# flights to the user. Two key parameters are the maximum altitude, and the furthest
# away we anticipate the flight being at its closest point to HOME. As those two
# parameters are manipulated in the settings, a histogram is displayed with one or
# potentially two series, showing the present and potentially prior-set distribution
# of flights, by hour throughout the day, over the last seven days, normalized to
# flights per day. This allows those parameters to be fine-tuned in a useful way.
<----SKIPPED LINES---->
#if running on raspberry, then need to prepend path to file names
if RASPBERRY_PI:
PICKLE_FLIGHTS = MESSAGEBOARD_PATH + PICKLE_FLIGHTS
PICKLE_DASHBOARD = MESSAGEBOARD_PATH + PICKLE_DASHBOARD
LOGFILE = MESSAGEBOARD_PATH + LOGFILE
PICKLE_DUMP_JSON_FILE = MESSAGEBOARD_PATH + PICKLE_DUMP_JSON_FILE
PICKLE_FA_JSON_FILE = MESSAGEBOARD_PATH + PICKLE_FA_JSON_FILE
CODE_REPOSITORY = MESSAGEBOARD_PATH
HISTOGRAM_CONFIG_FILE = WEBSERVER_PATH + HISTOGRAM_CONFIG_FILE
CONFIG_FILE = WEBSERVER_PATH + CONFIG_FILE
ROLLING_MESSAGE_FILE = WEBSERVER_PATH + ROLLING_MESSAGE_FILE
ALL_MESSAGE_FILE = WEBSERVER_PATH + ALL_MESSAGE_FILE
ROLLING_LOGFILE = WEBSERVER_PATH + ROLLING_LOGFILE
STDERR_FILE = WEBSERVER_PATH + STDERR_FILE
BACKUP_FILE = WEBSERVER_PATH + BACKUP_FILE
SERVICE_VERIFICATION_FILE = WEBSERVER_PATH + SERVICE_VERIFICATION_FILE
UPTIMES_FILE = WEBSERVER_PATH + UPTIMES_FILE
HISTOGRAM_IMAGE_PREFIX = WEBSERVER_PATH + WEBSERVER_IMAGE_FOLDER + HISTOGRAM_IMAGE_PREFIX
HISTOGRAM_EMPTY_IMAGE_FILE = (
WEBSERVER_PATH + WEBSERVER_IMAGE_FOLDER + HISTOGRAM_EMPTY_IMAGE_FILE)
HOURLY_IMAGE_FILE = WEBSERVER_PATH + WEBSERVER_IMAGE_FOLDER + HOURLY_IMAGE_FILE
VERSION_REPOSITORY = WEBSERVER_PATH + VERSION_REPOSITORY
TIMEZONE = 'US/Pacific' # timezone of display
TZ = pytz.timezone(TIMEZONE)
KNOWN_AIRPORTS = ('SJC', 'SFO', 'OAK') # iata codes that we don't need to expand
SPLITFLAP_CHARS_PER_LINE = 22
SPLITFLAP_LINE_COUNT = 6
DIRECTIONS_4 = ['N', 'E', 'S', 'W']
DIRECTIONS_8 = ['N', 'NE', 'E', 'SE', 'S', 'SW', 'W', 'NW']
DIRECTIONS_16 = ['N', 'NNE', 'NE', 'ENE', 'E', 'ESE', 'SE', 'SSE',
'S', 'SSW', 'SW', 'WSW', 'W', 'WNW', 'NW', 'NNW']
HOURS = ['12a', ' 1a', ' 2a', ' 3a', ' 4a', ' 5a', ' 6a', ' 7a',
' 8a', ' 9a', '10a', '11a', '12p', ' 1p', ' 2p', ' 3p',
' 4p', ' 5p', ' 6p', ' 7p', ' 8p', ' 9p', '10p', '11p']
SECONDS_IN_MINUTE = 60
MINUTES_IN_HOUR = 60
<----SKIPPED LINES---->
Args:
flights: the iterable of the raw data from which the histogram will be generated;
each element of the iterable is a dictionary, that contains at least the key
'now', and depending on other parameters, also potentially
'min_feet' amongst others.
which_histograms: string paramater indicating which histogram(s) to generate, which
can be either the special string 'all', or a string linked to a specific
histogram.
how_much_history: string parameter taking a value among ['today', '24h', '7d', '30d].
filename_prefix: this string indicates the file path and name prefix for the images
that are created. File names are created in the form [prefix]name.[suffix], i.e.:
if the prefix is histogram_ and the suffix is png, then the file name might be
histogram_aircraft.png.
filename_suffix: see above; also interpreted by savefig to generate the correct
format.
heartbeat: boolean indicating whether we should log heartbeats between each histogram
to make sure monitoring does not mistake this slow procedure for being hung; this
should be set to false if this is called from outside of messageboard.main.
Returns:
List of the names of the histograms generated.
"""
hours = HistogramSettingsHours(how_much_history)
histograms_to_generate = []
if which_histograms in ['destination', 'all']:
histograms_to_generate.append({'generate': 'destination'})
if which_histograms in ['origin', 'all']:
histograms_to_generate.append({'generate': 'origin'})
if which_histograms in ['hour', 'all']:
histograms_to_generate.append({'generate': 'hour'})
if which_histograms in ['airline', 'all']:
histograms_to_generate.append({'generate': 'airline', 'truncate': int(TRUNCATE/2)})
if which_histograms in ['aircraft', 'all']:
histograms_to_generate.append({'generate': 'aircraft'})
if which_histograms in ['altitude', 'all']:
histograms_to_generate.append({'generate': 'altitude', 'exhaustive': True})
if which_histograms in ['bearing', 'all']:
histograms_to_generate.append({'generate': 'bearing'})
if which_histograms in ['distance', 'all']:
histograms_to_generate.append({'generate': 'distance', 'exhaustive': True})
if which_histograms in ['day_of_week', 'all']:
histograms_to_generate.append({'generate': 'day_of_week'})
if which_histograms in ['day_of_month', 'all']:
histograms_to_generate.append({'generate': 'day_of_month'})
if which_histograms in ['speed', 'all']:
histograms_to_generate.append({'generate': 'speed', 'exhaustive': True})
if which_histograms in ['aircraft_length', 'all']:
histograms_to_generate.append({'generate': 'aircraft_length', 'exhaustive': True})
if which_histograms in ['vert_rate', 'all']:
histograms_to_generate.append({'generate': 'vert_rate', 'exhaustive': True})
for histogram in histograms_to_generate:
this_histogram = which_histograms
if this_histogram == 'all':
this_histogram = histogram['generate']
(key, sort, title, hours) = HistogramSettingsKeySortTitle(
this_histogram, hours, flights)
# if multiple histograms are getting generated, this might take a few seconds;
# logging a heartbeat with each histogram ensures that monitoring.py does not
# mistake this pause for a hang.
if heartbeat:
Heartbeat()
CreateSingleHistogramChart(
flights,
key,
sort,
title,
truncate=histogram.get('truncate', TRUNCATE),
hours=hours,
exhaustive=histogram.get('exhaustive', False))
filename = filename_prefix + histogram['generate'] + '.' + filename_suffix
matplotlib.pyplot.savefig(filename)
matplotlib.pyplot.close()
histograms_generated = [h['generate'] for h in histograms_to_generate]
return histograms_generated
def MessageboardHistograms(
flights,
which_histograms,
how_much_history,
max_screens,
data_summary):
"""Generates multiple split flap screen histograms.
Args:
flights: the iterable of the raw data from which the histogram will be generated;
each element of the iterable is a dictionary, that contains at least the key
'now', and depending on other parameters, also potentially
'min_feet' amongst others.
which_histograms: string paramater indicating which histogram(s) to generate, which
can be either the special string 'all', or a string linked to a specific
histogram.
how_much_history: string parameter taking a value among ['today', '24h', '7d', '30d].
<----SKIPPED LINES---->
created so that broken image links are not displayed in the webpage.
Args:
flights: List of flight attribute dictionaries.
histogram_settings: Dictionary of histogram parameters.
Returns:
List of histogram messages, if text-based histograms are selected; empty list
otherwise.
"""
histogram_messages = []
if histogram_settings['type'] in ('messageboard', 'both'):
histogram_messages = MessageboardHistograms(
flights,
histogram_settings['histogram'],
histogram_settings['histogram_history'],
histogram_settings['histogram_max_screens'],
histogram_settings.get('histogram_data_summary', False))
if histogram_settings['type'] in ('images', 'both'):
ImageHistograms(
flights,
histogram_settings['histogram'],
histogram_settings['histogram_history'])
return histogram_messages
def SaveFlightsByAltitudeDistanceCSV(
flights,
max_days=0,
filename='flights_by_alt_dist.csv',
precision=100):
"""Extracts hourly histogram into text file for a variety of altitudes and distances.
Generates a csv with 26 columns:
- col#1: altitude (in feet)
- col#2: distance (in feet)
- cols#3-26: hour of the day
The first row is a header row; subsequent rows list the number of flights that have
occurred in the last max_days with an altitude and min distance less than that identified
in the first two columns. Each row increments elevation or altitude by precision feet,
up to the max determined by the max altitude and max distance amongst all the flights.
<----SKIPPED LINES---->
|
01234567890123456789012345678901234567890123456789012345678901234567890123456789
102103104105106107108109110111112113114115116117118119120121122 123124125126127128129130131132133134135136137138139140141142 240241242243244245246247248249250251252253254255256257258259260 261262263264265266267268269270271272273274275276277278279280281282283284 4369437043714372437343744375437643774378437943804381438243834384438543864387438843894390439143924393439443954396439743984399440044014402440344044405440644074408440944104411441244134414441544164417441844194420442144224423442444254426442744284429443044314432443344344435443644374438443944404441444244434444444544464447444844494450 44514452445344544455445644574458445944604461446244634464446544664467446844694470 47264727472847294730473147324733473447354736473747384739474047414742474347444745474647474748474947504751475247534754475547564757475847594760476147624763476447654766476747684769477047714772477347744775477647774778477947804781478247834784478547864787478847894790479147924793479447954796479747984799480048014802 |
<----SKIPPED LINES---->
# This web-exposed file is used for non-error messages that might highlight data or
# code logic to check into. It is only cleared out manually.
LOGFILE = 'log.txt'
# Identical to the LOGFILE, except it includes just the most recent n lines. Newest
# lines are at the end.
ROLLING_LOGFILE = 'rolling_log.txt' #file for error messages
ROLLING_LOG_SIZE = 1000 # default number of lines which may be overridden by settings file
# Users can trigger .png histograms analogous to the text ones from the web interface;
# this is the folder (within WEBSERVER_PATH) where those files are placed
WEBSERVER_IMAGE_FOLDER = 'images/'
# Multiple histograms can be generated, i.e. for airline, aircraft, day of week, etc.
# The output files are named by the prefix & suffix, i.e.: prefix + type + . + suffix,
# as in histogram_aircraft.png. These names match up to the names expected by the html
# page that displays the images. Also, note that the suffix is interpreted by matplotlib
# to identify the image format to create.
HISTOGRAM_IMAGE_PREFIX = 'histogram_'
HISTOGRAM_IMAGE_SUFFIX = 'png'
HISTOGRAM_IMAGE_HTML = 'histograms.html'
# This file indicates a pending request for histograms - either png, text-based, or
# both; once it is processed, this file is deleted. The contents are concatenated key-value
# pairs, histogram=all;histogram_history=24h; etc.
HISTOGRAM_CONFIG_FILE = 'secure/histogram.txt'
HISTOGRAM_BOOLEANS = ('histogram_data_summary')
# This contains concatenated key-value configuration attributes in a similar format
# to the HISTOGRAM_CONFIG_FILE that are exposed to the user via the web interface or,
# for a subset of them, through the Arduino interface. They are polled at every iteration
# so that the most current value is always leveraged by the running software.
CONFIG_FILE = 'secure/settings.txt'
CONFIG_BOOLEANS = ('setting_screen_enabled', 'next_flight', 'reset_logs', 'log_jsons')
# A few key settings for the messageboard are its sensitivity to displaying flights -
# though it logs all flights within range, it may not be desirable to display all
# flights to the user. Two key parameters are the maximum altitude, and the furthest
# away we anticipate the flight being at its closest point to HOME. As those two
# parameters are manipulated in the settings, a histogram is displayed with one or
# potentially two series, showing the present and potentially prior-set distribution
# of flights, by hour throughout the day, over the last seven days, normalized to
# flights per day. This allows those parameters to be fine-tuned in a useful way.
<----SKIPPED LINES---->
#if running on raspberry, then need to prepend path to file names
if RASPBERRY_PI:
PICKLE_FLIGHTS = MESSAGEBOARD_PATH + PICKLE_FLIGHTS
PICKLE_DASHBOARD = MESSAGEBOARD_PATH + PICKLE_DASHBOARD
LOGFILE = MESSAGEBOARD_PATH + LOGFILE
PICKLE_DUMP_JSON_FILE = MESSAGEBOARD_PATH + PICKLE_DUMP_JSON_FILE
PICKLE_FA_JSON_FILE = MESSAGEBOARD_PATH + PICKLE_FA_JSON_FILE
CODE_REPOSITORY = MESSAGEBOARD_PATH
HISTOGRAM_CONFIG_FILE = WEBSERVER_PATH + HISTOGRAM_CONFIG_FILE
CONFIG_FILE = WEBSERVER_PATH + CONFIG_FILE
ROLLING_MESSAGE_FILE = WEBSERVER_PATH + ROLLING_MESSAGE_FILE
ALL_MESSAGE_FILE = WEBSERVER_PATH + ALL_MESSAGE_FILE
ROLLING_LOGFILE = WEBSERVER_PATH + ROLLING_LOGFILE
STDERR_FILE = WEBSERVER_PATH + STDERR_FILE
BACKUP_FILE = WEBSERVER_PATH + BACKUP_FILE
SERVICE_VERIFICATION_FILE = WEBSERVER_PATH + SERVICE_VERIFICATION_FILE
UPTIMES_FILE = WEBSERVER_PATH + UPTIMES_FILE
HISTOGRAM_IMAGE_PREFIX = WEBSERVER_PATH + WEBSERVER_IMAGE_FOLDER + HISTOGRAM_IMAGE_PREFIX
HISTOGRAM_IMAGE_HTML = WEBSERVER_PATH + HISTOGRAM_IMAGE_HTML
HOURLY_IMAGE_FILE = WEBSERVER_PATH + WEBSERVER_IMAGE_FOLDER + HOURLY_IMAGE_FILE
VERSION_REPOSITORY = WEBSERVER_PATH + VERSION_REPOSITORY
TIMEZONE = 'US/Pacific' # timezone of display
TZ = pytz.timezone(TIMEZONE)
KNOWN_AIRPORTS = ('SJC', 'SFO', 'OAK') # iata codes that we don't need to expand
SPLITFLAP_CHARS_PER_LINE = 22
SPLITFLAP_LINE_COUNT = 6
DIRECTIONS_4 = ['N', 'E', 'S', 'W']
DIRECTIONS_8 = ['N', 'NE', 'E', 'SE', 'S', 'SW', 'W', 'NW']
DIRECTIONS_16 = ['N', 'NNE', 'NE', 'ENE', 'E', 'ESE', 'SE', 'SSE',
'S', 'SSW', 'SW', 'WSW', 'W', 'WNW', 'NW', 'NNW']
HOURS = ['12a', ' 1a', ' 2a', ' 3a', ' 4a', ' 5a', ' 6a', ' 7a',
' 8a', ' 9a', '10a', '11a', '12p', ' 1p', ' 2p', ' 3p',
' 4p', ' 5p', ' 6p', ' 7p', ' 8p', ' 9p', '10p', '11p']
SECONDS_IN_MINUTE = 60
MINUTES_IN_HOUR = 60
<----SKIPPED LINES---->
Args:
flights: the iterable of the raw data from which the histogram will be generated;
each element of the iterable is a dictionary, that contains at least the key
'now', and depending on other parameters, also potentially
'min_feet' amongst others.
which_histograms: string paramater indicating which histogram(s) to generate, which
can be either the special string 'all', or a string linked to a specific
histogram.
how_much_history: string parameter taking a value among ['today', '24h', '7d', '30d].
filename_prefix: this string indicates the file path and name prefix for the images
that are created. File names are created in the form [prefix]name.[suffix], i.e.:
if the prefix is histogram_ and the suffix is png, then the file name might be
histogram_aircraft.png.
filename_suffix: see above; also interpreted by savefig to generate the correct
format.
heartbeat: boolean indicating whether we should log heartbeats between each histogram
to make sure monitoring does not mistake this slow procedure for being hung; this
should be set to false if this is called from outside of messageboard.main.
Returns:
List of 2-tuples of histograms generated, where the first element is the histogram
identifier (i.e.: destination), and the second element is the filename (i.e.:
histogram_destination.png).
"""
hours = HistogramSettingsHours(how_much_history)
histograms_to_generate = []
if which_histograms in ['destination', 'all']:
histograms_to_generate.append({'generate': 'destination'})
if which_histograms in ['origin', 'all']:
histograms_to_generate.append({'generate': 'origin'})
if which_histograms in ['hour', 'all']:
histograms_to_generate.append({'generate': 'hour'})
if which_histograms in ['airline', 'all']:
histograms_to_generate.append({'generate': 'airline', 'truncate': int(TRUNCATE/2)})
if which_histograms in ['aircraft', 'all']:
histograms_to_generate.append({'generate': 'aircraft'})
if which_histograms in ['altitude', 'all']:
histograms_to_generate.append({'generate': 'altitude', 'exhaustive': True})
if which_histograms in ['bearing', 'all']:
histograms_to_generate.append({'generate': 'bearing'})
if which_histograms in ['distance', 'all']:
histograms_to_generate.append({'generate': 'distance', 'exhaustive': True})
if which_histograms in ['day_of_week', 'all']:
histograms_to_generate.append({'generate': 'day_of_week'})
if which_histograms in ['day_of_month', 'all']:
histograms_to_generate.append({'generate': 'day_of_month'})
if which_histograms in ['speed', 'all']:
histograms_to_generate.append({'generate': 'speed', 'exhaustive': True})
if which_histograms in ['aircraft_length', 'all']:
histograms_to_generate.append({'generate': 'aircraft_length', 'exhaustive': True})
if which_histograms in ['vert_rate', 'all']:
histograms_to_generate.append({'generate': 'vert_rate', 'exhaustive': True})
histograms_generated = []
for histogram in histograms_to_generate:
this_histogram = which_histograms
if this_histogram == 'all':
this_histogram = histogram['generate']
(key, sort, title, hours) = HistogramSettingsKeySortTitle(
this_histogram, hours, flights)
# if multiple histograms are getting generated, this might take a few seconds;
# logging a heartbeat with each histogram ensures that monitoring.py does not
# mistake this pause for a hang.
if heartbeat:
Heartbeat()
CreateSingleHistogramChart(
flights,
key,
sort,
title,
truncate=histogram.get('truncate', TRUNCATE),
hours=hours,
exhaustive=histogram.get('exhaustive', False))
filename = filename_prefix + histogram['generate'] + '.' + filename_suffix
matplotlib.pyplot.savefig(filename)
matplotlib.pyplot.close()
histograms_generated.append((histogram['generate'], filename))
return histograms_generated
def MessageboardHistograms(
flights,
which_histograms,
how_much_history,
max_screens,
data_summary):
"""Generates multiple split flap screen histograms.
Args:
flights: the iterable of the raw data from which the histogram will be generated;
each element of the iterable is a dictionary, that contains at least the key
'now', and depending on other parameters, also potentially
'min_feet' amongst others.
which_histograms: string paramater indicating which histogram(s) to generate, which
can be either the special string 'all', or a string linked to a specific
histogram.
how_much_history: string parameter taking a value among ['today', '24h', '7d', '30d].
<----SKIPPED LINES---->
created so that broken image links are not displayed in the webpage.
Args:
flights: List of flight attribute dictionaries.
histogram_settings: Dictionary of histogram parameters.
Returns:
List of histogram messages, if text-based histograms are selected; empty list
otherwise.
"""
histogram_messages = []
if histogram_settings['type'] in ('messageboard', 'both'):
histogram_messages = MessageboardHistograms(
flights,
histogram_settings['histogram'],
histogram_settings['histogram_history'],
histogram_settings['histogram_max_screens'],
histogram_settings.get('histogram_data_summary', False))
if histogram_settings['type'] in ('images', 'both'):
# Since Google Chrome seems to ignore all instructions to not cache, we need
# to make sure we do not reuse file names - hence the epoch_string - and then
# we need to 1) update the histograms.html file with the correct file links, and
# 2) delete the images that are now obsolete.
epoch_string = '%d_' % round(time.time())
generated_histograms = ImageHistograms(
flights,
histogram_settings['histogram'],
histogram_settings['histogram_history'],
filename_prefix=HISTOGRAM_IMAGE_PREFIX + epoch_string)
html_lines = ReadFile(HISTOGRAM_IMAGE_HTML).split('\n')
replaced_images = []
for identifier, new_filename in generated_histograms:
# for each histogram, find the html_line with the matching id
# Example line: <img id='destination' src='images/histogram_destination.png'><p>
n, line = None, None # addresses pylint complaint
for n, line in enumerate(html_lines):
if identifier in line:
break
start_char = line.find(WEBSERVER_IMAGE_FOLDER) + len(WEBSERVER_IMAGE_FOLDER)
end_character = (
line.find(HISTOGRAM_IMAGE_SUFFIX, start_char) + len(HISTOGRAM_IMAGE_SUFFIX))
old_filename = line[start_char:end_character]
line = line.replace(old_filename, new_filename)
html_lines[n] = line
replaced_images.append(line[start_char:end_character])
new_html = '\n'.join(html_lines)
WriteFile(HISTOGRAM_IMAGE_HTML, new_html)
# Remove those obsoleted files
for f in old_filename:
if os.path.exists(f):
try:
os.remove(f)
except PermissionError:
pass
return histogram_messages
def SaveFlightsByAltitudeDistanceCSV(
flights,
max_days=0,
filename='flights_by_alt_dist.csv',
precision=100):
"""Extracts hourly histogram into text file for a variety of altitudes and distances.
Generates a csv with 26 columns:
- col#1: altitude (in feet)
- col#2: distance (in feet)
- cols#3-26: hour of the day
The first row is a header row; subsequent rows list the number of flights that have
occurred in the last max_days with an altitude and min distance less than that identified
in the first two columns. Each row increments elevation or altitude by precision feet,
up to the max determined by the max altitude and max distance amongst all the flights.
<----SKIPPED LINES---->
|