From 6af5fd20f116dbf03514ed825b22cc65ada40fec Mon Sep 17 00:00:00 2001 From: Wilfred Tyler Gee Date: Sun, 18 Aug 2019 15:50:02 +1000 Subject: [PATCH 1/6] Weather sensor & plot updates * Ability to specify `db_type` with a default of `file`. * Remove unused `get_reading`. * Messaging ports come from config instead of hard-coded. * Easier date parsing on plotting. * Plotting skips invalid entries. * Masked entries are filled with zeros (!) --- peas/weather.py | 27 ++++++++++----------------- scripts/plot_weather.py | 30 +++++++++++++++++------------- 2 files changed, 27 insertions(+), 30 deletions(-) diff --git a/peas/weather.py b/peas/weather.py index 6044cf382..a21dd0e48 100755 --- a/peas/weather.py +++ b/peas/weather.py @@ -18,9 +18,9 @@ from .PID import PID -def get_mongodb(): +def get_db(db_type='file'): from pocs.utils.database import PanDB - return PanDB() + return PanDB(db_type=db_type) def movingaverage(interval, window_size): @@ -101,7 +101,7 @@ class AAGCloudSensor(object): """ - def __init__(self, serial_address=None, store_result=True): + def __init__(self, serial_address=None, store_result=True, db_type='file'): self.config = load_config(config_files='peas') self.logger = get_root_logger() @@ -112,7 +112,7 @@ def __init__(self, serial_address=None, store_result=True): self.db = None if store_result: - self.db = get_mongodb() + self.db = get_db(db_type=db_type) self.messaging = None @@ -249,18 +249,6 @@ def __init__(self, serial_address=None, store_result=True): self.logger.warning(' Failed to get Serial Number') sys.exit(1) - def get_reading(self): - """ Calls commands to be performed each time through the loop """ - weather_data = dict() - - if self.db is None: - self.db = get_mongodb() - else: - weather_data = self.update_weather() - self.calculate_and_set_PWM() - - return weather_data - def send(self, send, delay=0.100): found_command = False @@ -606,7 +594,12 @@ def get_wind_speed(self, n=3): def send_message(self, msg, topic='weather'): if self.messaging is None: - self.messaging = PanMessaging.create_publisher(6510) + try: + msg_port = self.config['messaging']['msg_port'] + except KeyError: + return + + self.messaging = PanMessaging.create_publisher(msg_port) self.messaging.send_message(topic, msg) diff --git a/scripts/plot_weather.py b/scripts/plot_weather.py index ec405c176..b0e22e78b 100755 --- a/scripts/plot_weather.py +++ b/scripts/plot_weather.py @@ -5,9 +5,11 @@ import pandas as pd import sys import warnings +from contextlib import suppress from datetime import datetime as dt from datetime import timedelta as tdelta +from dateutil.parser import parse as date_parser from astropy.table import Table from astropy.time import Time @@ -17,9 +19,8 @@ from pocs.utils.config import load_config from pocs.utils import serializers as json_util +from pocs.utils import error -import matplotlib as mpl -mpl.use('Agg') from matplotlib import pyplot as plt from matplotlib.dates import DateFormatter from matplotlib.dates import HourLocator @@ -60,8 +61,7 @@ def __init__(self, date_string=None, data_file=None, *args, **kwargs): else: self.today = False - self.date = dt.strptime('{} 23:59:59'.format(date_string), - '%Y%m%dUT %H:%M:%S') + self.date = date_parser(f'{date_string} 23:59:59') self.date_string = date_string self.start = dt(self.date.year, self.date.month, self.date.day, 0, 0, 0, 0) self.end = dt(self.date.year, self.date.month, self.date.day, 23, 59, 59, 0) @@ -142,8 +142,12 @@ def get_table_data(self, data_file): weather_entries = list() with open(data_file) as df: for entry in df: - rec0 = json_util.loads(entry) - weather_entries.append({**rec0['data']}) + try: + rec0 = json_util.loads(entry) + weather_entries.append({**rec0['data']}) + except Exception: + # Skip invalid entries. + pass table = pd.DataFrame(weather_entries) # Fix bad dtype conversion @@ -302,9 +306,9 @@ def plot_cloudiness_vs_time(self): print('Plot Temperature Difference vs. Time') td_axes = plt.axes(self.plot_positions[1][0]) - sky_temp_C = self.table['sky_temp_C'] - ambient_temp_C = self.table['ambient_temp_C'] - sky_condition = self.table['sky_condition'] + sky_temp_C = self.table['sky_temp_C'].filled(0) + ambient_temp_C = self.table['ambient_temp_C'].filled(0) + sky_condition = self.table['sky_condition'].filled(0) temp_diff = np.array(sky_temp_C) - np.array(ambient_temp_C) @@ -385,10 +389,10 @@ def plot_windspeed_vs_time(self): print('Plot Wind Speed vs. Time') w_axes = plt.axes(self.plot_positions[2][0]) - wind_speed = self.table['wind_speed_KPH'] + wind_speed = self.table['wind_speed_KPH'].filled(0) wind_mavg = moving_average(wind_speed, 9) matime, wind_mavg = moving_averagexy(self.time, wind_speed, 9) - wind_condition = self.table['wind_condition'] + wind_condition = self.table['wind_condition'].filled(0) w_axes.plot_date(self.time, wind_speed, 'ko', alpha=0.5, markersize=2, markeredgewidth=0, @@ -507,8 +511,8 @@ def plot_rain_freq_vs_time(self): print('Plot Rain Frequency vs. Time') rf_axes = plt.axes(self.plot_positions[3][0]) - rf_value = self.table['rain_frequency'] - rain_condition = self.table['rain_condition'] + rf_value = self.table['rain_frequency'].filled(0) + rain_condition = self.table['rain_condition'].filled(0) rf_axes.plot_date(self.time, rf_value, 'ko-', label='Rain', markersize=2, markeredgewidth=0, From 19475a1f281b7730858cb8dddc9c74832b35cc30 Mon Sep 17 00:00:00 2001 From: Wilfred Tyler Gee Date: Mon, 19 Aug 2019 13:56:58 +1000 Subject: [PATCH 2/6] Remove unused plotting helper script --- scripts/plot_weather.sh | 51 ----------------------------------------- 1 file changed, 51 deletions(-) delete mode 100755 scripts/plot_weather.sh diff --git a/scripts/plot_weather.sh b/scripts/plot_weather.sh deleted file mode 100755 index b68ae142d..000000000 --- a/scripts/plot_weather.sh +++ /dev/null @@ -1,51 +0,0 @@ -#!/bin/bash - -# Runs scripts/plog_weather.py, which will generate an image in -# $PANDIR/weather_plots/. - -# We need the shell to have the the PANOPTES environment setup. - -echo "Running ${BASH_SOURCE[0]} at $(date)" - -if [[ -z "$PANDIR" || -z "$POCS" || -z "$PANLOG" ]] ; then - echo "The PANOPTES environment variables must be set." - echo "This script should be run from a login shell." - exit 1 -fi - -echo "Setting up logging..." - -# Setup a directory for the log file from this script and from those -# it invokes. By creating a unique directory per startup, we make it -# easier to view the group of files from a single reboot. -export LOG_DIR_SLASH="${PANLOG}/per-run/$(basename "${BASH_SOURCE[0]}" .sh)/" -mkdir -p "${LOG_DIR_SLASH}" - -LOG_NAME="$(basename "${BASH_SOURCE[0]}" .sh).$(date +%Y%m%d-%H%M%S-%Z).log" -LOG_FILE="${LOG_DIR_SLASH}${LOG_NAME}" - -echo "Will log to ${LOG_FILE}" - -exec 2> "${LOG_FILE}" # send stderr to a log file -exec 1>&2 # send stdout to the same log file -set +x - -# Record a bunch of environment variables into the log file. This -# helps us later if we need to debug the execution of this script -# and those it invokes. -echo "Running ${BASH_SOURCE[0]} at $(date)" -echo "Current dir: $(pwd)" -echo "Current user: $(whoami)" -echo "USER: ${USER}" -echo "LOGNAME: ${LOGNAME}" -echo "PATH: ${PATH}" -echo "PANUSER: ${PANUSER}" -echo "PANDIR: ${PANDIR}" -echo "PANLOG: ${PANLOG}" -echo "POCS: ${POCS}" -echo "PAWS: ${PAWS}" -echo "PIAA: ${PIAA}" - -set -x - -"${POCS}/scripts/plot_weather.py" From 68f1678d1812bb41c81512b134c1b66142651f35 Mon Sep 17 00:00:00 2001 From: Wilfred Tyler Gee Date: Mon, 19 Aug 2019 13:57:58 +1000 Subject: [PATCH 3/6] Remove plotly and other unsused items. --- scripts/plot_weather.py | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/scripts/plot_weather.py b/scripts/plot_weather.py index b0e22e78b..7f874eb1d 100755 --- a/scripts/plot_weather.py +++ b/scripts/plot_weather.py @@ -5,7 +5,6 @@ import pandas as pd import sys import warnings -from contextlib import suppress from datetime import datetime as dt from datetime import timedelta as tdelta @@ -19,7 +18,6 @@ from pocs.utils.config import load_config from pocs.utils import serializers as json_util -from pocs.utils import error from matplotlib import pyplot as plt from matplotlib.dates import DateFormatter @@ -97,7 +95,7 @@ def make_plot(self, output_file=None): self.lhstart.isoformat(), self.lhend.isoformat())) self.dpi = self.kwargs.get('dpi', 72) self.fig = plt.figure(figsize=(20, 12), dpi=self.dpi) -# self.axes = plt.gca() + self.hours = HourLocator(byhour=range(24), interval=1) self.hours_fmt = DateFormatter('%H') self.mins = MinuteLocator(range(0, 60, 15)) @@ -758,15 +756,7 @@ def moving_averagexy(x, y, window_size): help="Filename for data file") parser.add_argument("-o", "--plot_file", type=str, dest="plot_file", default=None, help="Filename for generated plot") - parser.add_argument('--plotly-user', help="Username for plotly publishing") - parser.add_argument('--plotly-api-key', help="API for plotly publishing") args = parser.parse_args() wp = WeatherPlotter(date_string=args.date, data_file=args.data_file) wp.make_plot(args.plot_file) - - if args.plotly_user and args.plotly_api_key: - from plotly import plotly - plotly.sign_in(args.plotly_user, args.plotly_api_key) - url = plotly.plot_mpl(wp.fig) - print('Plotly url: {}'.format(url)) From 8e4f43c08127061497adb102705c37814ad9c367 Mon Sep 17 00:00:00 2001 From: Wilfred Tyler Gee Date: Mon, 19 Aug 2019 15:22:03 +1000 Subject: [PATCH 4/6] Weather plot updates * Use only pandas tables, not astropy tables (makes working with dates easier). * Filter rows to only include relevant dates (speeds things up). * Use logger rather than print statements. * Update matplotlib date converters. * Create location for plotting using `create_location_from_config`. * Cleaned up date formatting for log output. --- scripts/plot_weather.py | 148 ++++++++++++++++++++-------------------- 1 file changed, 73 insertions(+), 75 deletions(-) diff --git a/scripts/plot_weather.py b/scripts/plot_weather.py index 7f874eb1d..686845de6 100755 --- a/scripts/plot_weather.py +++ b/scripts/plot_weather.py @@ -1,8 +1,6 @@ #!/usr/bin/env python3 -import numpy as np import os -import pandas as pd import sys import warnings @@ -10,14 +8,9 @@ from datetime import timedelta as tdelta from dateutil.parser import parse as date_parser -from astropy.table import Table -from astropy.time import Time - -from astroplan import Observer -from astropy.coordinates import EarthLocation - -from pocs.utils.config import load_config -from pocs.utils import serializers as json_util +import numpy as np +import pandas as pd +from pandas.plotting import register_matplotlib_converters from matplotlib import pyplot as plt from matplotlib.dates import DateFormatter @@ -25,6 +18,18 @@ from matplotlib.dates import MinuteLocator from matplotlib.ticker import FormatStrFormatter from matplotlib.ticker import MultipleLocator + +from astropy.table import Table +from astropy.time import Time + +from pocs.utils.config import load_config +from pocs.utils.logger import get_root_logger +from pocs.utils import serializers as json_util +from pocs.utils.location import create_location_from_config + +logger = get_root_logger() + +register_matplotlib_converters() plt.ioff() plt.style.use('classic') @@ -42,9 +47,8 @@ def __init__(self, date_string=None, data_file=None, *args, **kwargs): self.args = args self.kwargs = kwargs - config = load_config(config_files=['peas']) + config = load_config(config_files=['pocs', 'peas']) self.cfg = config['weather']['plot'] - location_cfg = config.get('location', None) self.thresholds = config['weather'].get('aag_cloud', None) @@ -59,43 +63,48 @@ def __init__(self, date_string=None, data_file=None, *args, **kwargs): else: self.today = False - self.date = date_parser(f'{date_string} 23:59:59') + self.date = date_parser('{date_string} 23:59:59') self.date_string = date_string self.start = dt(self.date.year, self.date.month, self.date.day, 0, 0, 0, 0) self.end = dt(self.date.year, self.date.month, self.date.day, 23, 59, 59, 0) - print('Creating weather plotter for {}'.format(self.date_string)) + logger.info(f'Creating weather plotter for {date_string}') - self.twilights = self.get_twilights(location_cfg) + self.location = create_location_from_config(config) + self.twilights = self.get_twilights() self.table = self.get_table_data(data_file) + logger.debug(f'Found {len(self.table)} weather entries.') + + # Filter by date + logger.debug(f'Filtering table rows for {self.date_string}') + self.table = self.table.loc[self.start:self.end] if self.table is None: warnings.warn("No data") sys.exit(0) - self.time = pd.to_datetime(self.table['date']) - first = self.time[0].isoformat() - last = self.time[-1].isoformat() - print(' Retrieved {} entries between {} and {}'.format( - len(self.table), first, last)) - - if self.today: - self.current_values = self.table[-1] - else: - self.current_values = None + self.time = self.table.index + self.date_format = '%Y-%m-%d %H:%m:%S' + first = f'{self.time[0]:{self.date_format}}' + last = f'{self.time[-1]:{self.date_format}}' + logger.debug(f'Retrieved {len(self.table)} entries between {first} and {last}') def make_plot(self, output_file=None): # ------------------------------------------------------------------------- # Plot a day's weather # ------------------------------------------------------------------------- - print(' Setting up plot for time range: {} to {}'.format( - self.start.isoformat(), self.end.isoformat())) + start_time = f'{self.start:{self.date_format}}' + start_hour = f'{self.lhstart:{self.date_format}}' + end_time = f'{self.end:{self.date_format}}' + end_hour = f'{self.lhend:{self.date_format}}' + + logger.debug(f'Setting up plot for time range: {start_time} to {end_time}') if self.today: - print(' Will generate last hour plot for time range: {} to {}'.format( - self.lhstart.isoformat(), self.lhend.isoformat())) + logger.debug(f'Will generate last hour plot: {start_hour} to {end_hour}') + self.dpi = self.kwargs.get('dpi', 72) self.fig = plt.figure(figsize=(20, 12), dpi=self.dpi) - +# self.axes = plt.gca() self.hours = HourLocator(byhour=range(24), interval=1) self.hours_fmt = DateFormatter('%H') self.mins = MinuteLocator(range(0, 60, 15)) @@ -150,17 +159,16 @@ def get_table_data(self, data_file): table = pd.DataFrame(weather_entries) # Fix bad dtype conversion table.rain_sensor_temp_C = pd.to_numeric(table.rain_sensor_temp_C) - table = Table.from_pandas(table) else: - table = Table.from_pandas(pd.read_csv(data_file, parse_dates=True)) + table = pd.read_csv(data_file, parse_dates=True) else: # ------------------------------------------------------------------------- # Grab data from Mongo # ------------------------------------------------------------------------- import pymongo - from pocs.utils.database import PanDB + from panoptes.utils.database import PanDB - print(' Retrieving data from Mongo database') + logger.debug('Retrieving data from Mongo database') db = PanDB() entries = [x for x in db.weather.find( {'date': {'$gt': self.start, '$lt': self.end}}).sort([ @@ -178,24 +186,14 @@ def get_table_data(self, data_file): table.add_row(data) - table.sort('date') + table.index = pd.to_datetime(table['date']) return table - def get_twilights(self, config=None): + def get_twilights(self): """ Determine sunrise and sunset times """ - print(' Determining sunrise, sunset, and twilight times') - - if config is None: - from pocs.utils.config import load_config as pocs_config - config = pocs_config()['location'] + logger.debug('Determining sunrise, sunset, and twilight times') - location = EarthLocation( - lat=config['latitude'], - lon=config['longitude'], - height=config['elevation'], - ) - obs = Observer(location=location, name='PANOPTES', - timezone=config['timezone']) + obs = self.location['observer'] sunset = obs.sun_set_time(Time(self.start), which='next').datetime sunrise = obs.sun_rise_time(Time(self.start), which='next').datetime @@ -227,7 +225,7 @@ def get_twilights(self, config=None): def plot_ambient_vs_time(self): """ Ambient Temperature vs Time """ - print('Plot Ambient Temperature vs. Time') + logger.debug('Plot Ambient Temperature vs. Time') t_axes = plt.axes(self.plot_positions[0][0]) if self.today: @@ -301,12 +299,12 @@ def plot_ambient_vs_time(self): def plot_cloudiness_vs_time(self): """ Cloudiness vs Time """ - print('Plot Temperature Difference vs. Time') + logger.debug('Plot Temperature Difference vs. Time') td_axes = plt.axes(self.plot_positions[1][0]) - sky_temp_C = self.table['sky_temp_C'].filled(0) - ambient_temp_C = self.table['ambient_temp_C'].filled(0) - sky_condition = self.table['sky_condition'].filled(0) + sky_temp_C = self.table['sky_temp_C'] + ambient_temp_C = self.table['ambient_temp_C'] + sky_condition = self.table['sky_condition'] temp_diff = np.array(sky_temp_C) - np.array(ambient_temp_C) @@ -314,13 +312,13 @@ def plot_cloudiness_vs_time(self): markersize=2, markeredgewidth=0, drawstyle="default") - wclear = [(x.strip() == 'Clear') for x in sky_condition.data] + wclear = [(x.strip() == 'Clear') for x in sky_condition] plt.fill_between(self.time, -60, temp_diff, where=wclear, color='green', alpha=0.5) - wcloudy = [(x.strip() == 'Cloudy') for x in sky_condition.data] + wcloudy = [(x.strip() == 'Cloudy') for x in sky_condition] plt.fill_between(self.time, -60, temp_diff, where=wcloudy, color='yellow', alpha=0.5) - wvcloudy = [(x.strip() == 'Very Cloudy') for x in sky_condition.data] + wvcloudy = [(x.strip() == 'Very Cloudy') for x in sky_condition] plt.fill_between(self.time, -60, temp_diff, where=wvcloudy, color='red', alpha=0.5) if self.thresholds: @@ -384,13 +382,13 @@ def plot_cloudiness_vs_time(self): def plot_windspeed_vs_time(self): """ Windspeed vs Time """ - print('Plot Wind Speed vs. Time') + logger.debug('Plot Wind Speed vs. Time') w_axes = plt.axes(self.plot_positions[2][0]) - wind_speed = self.table['wind_speed_KPH'].filled(0) + wind_speed = self.table['wind_speed_KPH'] wind_mavg = moving_average(wind_speed, 9) matime, wind_mavg = moving_averagexy(self.time, wind_speed, 9) - wind_condition = self.table['wind_condition'].filled(0) + wind_condition = self.table['wind_condition'] w_axes.plot_date(self.time, wind_speed, 'ko', alpha=0.5, markersize=2, markeredgewidth=0, @@ -401,13 +399,13 @@ def plot_windspeed_vs_time(self): linewidth=3, alpha=0.5, drawstyle="default") w_axes.plot_date([self.start, self.end], [0, 0], 'k-', ms=1) - wcalm = [(x.strip() == 'Calm') for x in wind_condition.data] + wcalm = [(x.strip() == 'Calm') for x in wind_condition] w_axes.fill_between(self.time, -5, wind_speed, where=wcalm, color='green', alpha=0.5) - wwindy = [(x.strip() == 'Windy') for x in wind_condition.data] + wwindy = [(x.strip() == 'Windy') for x in wind_condition] w_axes.fill_between(self.time, -5, wind_speed, where=wwindy, color='yellow', alpha=0.5) - wvwindy = [(x.strip() == 'Very Windy') for x in wind_condition.data] + wvwindy = [(x.strip() == 'Very Windy') for x in wind_condition] w_axes.fill_between(self.time, -5, wind_speed, where=wvwindy, color='red', alpha=0.5) @@ -506,23 +504,23 @@ def plot_windspeed_vs_time(self): def plot_rain_freq_vs_time(self): """ Rain Frequency vs Time """ - print('Plot Rain Frequency vs. Time') + logger.debug('Plot Rain Frequency vs. Time') rf_axes = plt.axes(self.plot_positions[3][0]) - rf_value = self.table['rain_frequency'].filled(0) - rain_condition = self.table['rain_condition'].filled(0) + rf_value = self.table['rain_frequency'] + rain_condition = self.table['rain_condition'] rf_axes.plot_date(self.time, rf_value, 'ko-', label='Rain', markersize=2, markeredgewidth=0, drawstyle="default") - wdry = [(x.strip() == 'Dry') for x in rain_condition.data] + wdry = [(x.strip() == 'Dry') for x in rain_condition] rf_axes.fill_between(self.time, 0, rf_value, where=wdry, color='green', alpha=0.5) - wwet = [(x.strip() == 'Wet') for x in rain_condition.data] + wwet = [(x.strip() == 'Wet') for x in rain_condition] rf_axes.fill_between(self.time, 0, rf_value, where=wwet, color='orange', alpha=0.5) - wrain = [(x.strip() == 'Rain') for x in rain_condition.data] + wrain = [(x.strip() == 'Rain') for x in rain_condition] rf_axes.fill_between(self.time, 0, rf_value, where=wrain, color='red', alpha=0.5) @@ -582,7 +580,7 @@ def plot_rain_freq_vs_time(self): def plot_safety_vs_time(self): """ Plot Safety Values """ - print('Plot Safe/Unsafe vs. Time') + logger.debug('Plot Safe/Unsafe vs. Time') safe_axes = plt.axes(self.plot_positions[4][0]) safe_value = [int(x) for x in self.table['safe']] @@ -591,10 +589,10 @@ def plot_safety_vs_time(self): markersize=2, markeredgewidth=0, drawstyle="default") safe_axes.fill_between(self.time, -1, safe_value, - where=(self.table['safe'].data), + where=(self.table['safe']), color='green', alpha=0.5) safe_axes.fill_between(self.time, -1, safe_value, - where=(~self.table['safe'].data), + where=(~self.table['safe']), color='red', alpha=0.5) plt.ylabel("Safe") plt.xlim(self.start, self.end) @@ -612,10 +610,10 @@ def plot_safety_vs_time(self): markersize=4, markeredgewidth=0, drawstyle="default") safelh_axes.fill_between(self.time, -1, safe_value, - where=(self.table['safe'].data), + where=(self.table['safe']), color='green', alpha=0.5) safelh_axes.fill_between(self.time, -1, safe_value, - where=(~self.table['safe'].data), + where=(~self.table['safe']), color='red', alpha=0.5) plt.plot_date([self.date, self.date], [-0.1, 1.1], 'g-', alpha=0.4) @@ -644,7 +642,7 @@ def plot_safety_vs_time(self): def plot_pwm_vs_time(self): """ Plot Heater values """ - print('Plot PWM Value vs. Time') + logger.debug('Plot PWM Value vs. Time') pwm_axes = plt.axes(self.plot_positions[5][0]) plt.ylabel("Heater (%)") plt.ylim(self.cfg['pwm_limits']) @@ -715,7 +713,7 @@ def save_plot(self, plot_filename=None): if not os.path.exists(plot_dir): os.makedirs(plot_dir) - print('Saving Figure: {}'.format(plot_filename)) + logger.info('Saving weather plot: {}'.format(plot_filename)) self.fig.savefig( plot_filename, dpi=self.dpi, From 72f4d860d6eff10576b9060f311011f8e5c491a1 Mon Sep 17 00:00:00 2001 From: Wilfred Tyler Gee Date: Mon, 19 Aug 2019 15:45:39 +1000 Subject: [PATCH 5/6] Use OO interface for all plotting. --- scripts/plot_weather.py | 260 ++++++++++++++++++++-------------------- 1 file changed, 130 insertions(+), 130 deletions(-) diff --git a/scripts/plot_weather.py b/scripts/plot_weather.py index 686845de6..a9eb6f346 100755 --- a/scripts/plot_weather.py +++ b/scripts/plot_weather.py @@ -123,6 +123,8 @@ def make_plot(self, output_file=None): self.plot_safety_vs_time() self.plot_pwm_vs_time() self.save_plot(plot_filename=output_file) + # Close all figures to free memory. + plt.close('all') def get_table_data(self, data_file): """ Get the table data @@ -227,26 +229,25 @@ def plot_ambient_vs_time(self): """ Ambient Temperature vs Time """ logger.debug('Plot Ambient Temperature vs. Time') - t_axes = plt.axes(self.plot_positions[0][0]) + t_axes = self.fig.add_axes(self.plot_positions[0][0]) if self.today: time_title = self.date else: time_title = self.end - plt.title('Weather for {} at {}'.format(self.date_string, - time_title.strftime('%H:%M:%S UT'))) + t_axes.set_title(f'Weather for {self.date_string} at {time_title:%H:%M:%S}') amb_temp = self.table['ambient_temp_C'] - plt.plot_date(self.time, amb_temp, 'ko', - markersize=2, markeredgewidth=0, drawstyle="default") + t_axes.plot_date(self.time, amb_temp, 'ko', + markersize=2, markeredgewidth=0, drawstyle="default") try: max_temp = max(amb_temp) min_temp = min(amb_temp) label_time = self.end - tdelta(0, 6 * 60 * 60) label_temp = label_pos(self.cfg['amb_temp_limits']) - plt.annotate('Low: {:4.1f} $^\circ$C, High: {:4.1f} $^\circ$C'.format( + t_axes.annotate('Low: {:4.1f} $^\circ$C, High: {:4.1f} $^\circ$C'.format( min_temp, max_temp), xy=(label_time, max_temp), xytext=(label_time, label_temp), @@ -255,27 +256,27 @@ def plot_ambient_vs_time(self): except Exception: pass - plt.ylabel("Ambient Temp. (C)") - plt.grid(which='major', color='k') - plt.yticks(range(-100, 100, 10)) - plt.xlim(self.start, self.end) - plt.ylim(self.cfg['amb_temp_limits']) + t_axes.set_ylabel("Ambient Temp. (C)") + t_axes.grid(which='major', color='k') + t_axes.set_yticks(range(-100, 100, 10)) + t_axes.set_xlim(self.start, self.end) + t_axes.set_ylim(self.cfg['amb_temp_limits']) t_axes.xaxis.set_major_locator(self.hours) t_axes.xaxis.set_major_formatter(self.hours_fmt) for i, twi in enumerate(self.twilights): if i > 0: - plt.axvspan(self.twilights[i - 1][0], self.twilights[i][0], - ymin=0, ymax=1, color='blue', alpha=twi[2]) + t_axes.axvspan(self.twilights[i - 1][0], self.twilights[i][0], + ymin=0, ymax=1, color='blue', alpha=twi[2]) if self.today: - tlh_axes = plt.axes(self.plot_positions[0][1]) - plt.title('Last Hour') - plt.plot_date(self.time, amb_temp, 'ko', - markersize=4, markeredgewidth=0, - drawstyle="default") - plt.plot_date([self.date, self.date], self.cfg['amb_temp_limits'], - 'g-', alpha=0.4) + tlh_axes = self.fig.add_axes(self.plot_positions[0][1]) + tlh_axes.set_title('Last Hour') + tlh_axes.plot_date(self.time, amb_temp, 'ko', + markersize=4, markeredgewidth=0, + drawstyle="default") + tlh_axes.plot_date([self.date, self.date], self.cfg['amb_temp_limits'], + 'g-', alpha=0.4) try: current_amb_temp = self.current_values['data']['ambient_temp_C'] current_time = self.current_values['date'] @@ -289,18 +290,18 @@ def plot_ambient_vs_time(self): except Exception: pass - plt.grid(which='major', color='k') - plt.yticks(range(-100, 100, 10)) + tlh_axes.grid(which='major', color='k') + tlh_axes.set_yticks(range(-100, 100, 10)) tlh_axes.xaxis.set_major_locator(self.mins) tlh_axes.xaxis.set_major_formatter(self.mins_fmt) tlh_axes.yaxis.set_ticklabels([]) - plt.xlim(self.lhstart, self.lhend) - plt.ylim(self.cfg['amb_temp_limits']) + tlh_axes.set_xlim(self.lhstart, self.lhend) + tlh_axes.set_ylim(self.cfg['amb_temp_limits']) def plot_cloudiness_vs_time(self): """ Cloudiness vs Time """ logger.debug('Plot Temperature Difference vs. Time') - td_axes = plt.axes(self.plot_positions[1][0]) + td_axes = self.fig.add_axes(self.plot_positions[1][0]) sky_temp_C = self.table['sky_temp_C'] ambient_temp_C = self.table['ambient_temp_C'] @@ -308,55 +309,55 @@ def plot_cloudiness_vs_time(self): temp_diff = np.array(sky_temp_C) - np.array(ambient_temp_C) - plt.plot_date(self.time, temp_diff, 'ko-', label='Cloudiness', - markersize=2, markeredgewidth=0, - drawstyle="default") + td_axes.plot_date(self.time, temp_diff, 'ko-', label='Cloudiness', + markersize=2, markeredgewidth=0, + drawstyle="default") wclear = [(x.strip() == 'Clear') for x in sky_condition] - plt.fill_between(self.time, -60, temp_diff, where=wclear, color='green', alpha=0.5) + td_axes.fill_between(self.time, -60, temp_diff, where=wclear, color='green', alpha=0.5) wcloudy = [(x.strip() == 'Cloudy') for x in sky_condition] - plt.fill_between(self.time, -60, temp_diff, where=wcloudy, color='yellow', alpha=0.5) + td_axes.fill_between(self.time, -60, temp_diff, where=wcloudy, color='yellow', alpha=0.5) wvcloudy = [(x.strip() == 'Very Cloudy') for x in sky_condition] - plt.fill_between(self.time, -60, temp_diff, where=wvcloudy, color='red', alpha=0.5) + td_axes.fill_between(self.time, -60, temp_diff, where=wvcloudy, color='red', alpha=0.5) if self.thresholds: st = self.thresholds.get('threshold_very_cloudy', None) if st: - plt.plot_date([self.start, self.end], [st, st], 'r-', - markersize=2, markeredgewidth=0, alpha=0.3, - drawstyle="default") - - plt.ylabel("Cloudiness") - plt.grid(which='major', color='k') - plt.yticks(range(-100, 100, 10)) - plt.xlim(self.start, self.end) - plt.ylim(self.cfg['cloudiness_limits']) + td_axes.plot_date([self.start, self.end], [st, st], 'r-', + markersize=2, markeredgewidth=0, alpha=0.3, + drawstyle="default") + + td_axes.set_ylabel("Cloudiness") + td_axes.grid(which='major', color='k') + td_axes.set_yticks(range(-100, 100, 10)) + td_axes.set_xlim(self.start, self.end) + td_axes.set_ylim(self.cfg['cloudiness_limits']) td_axes.xaxis.set_major_locator(self.hours) td_axes.xaxis.set_major_formatter(self.hours_fmt) td_axes.xaxis.set_ticklabels([]) if self.today: - tdlh_axes = plt.axes(self.plot_positions[1][1]) + tdlh_axes = self.fig.add_axes(self.plot_positions[1][1]) tdlh_axes.plot_date(self.time, temp_diff, 'ko-', label='Cloudiness', markersize=4, markeredgewidth=0, drawstyle="default") - plt.fill_between(self.time, -60, temp_diff, where=wclear, - color='green', alpha=0.5) - plt.fill_between(self.time, -60, temp_diff, where=wcloudy, - color='yellow', alpha=0.5) - plt.fill_between(self.time, -60, temp_diff, where=wvcloudy, - color='red', alpha=0.5) - plt.plot_date([self.date, self.date], self.cfg['cloudiness_limits'], - 'g-', alpha=0.4) + tdlh_axes.fill_between(self.time, -60, temp_diff, where=wclear, + color='green', alpha=0.5) + tdlh_axes.fill_between(self.time, -60, temp_diff, where=wcloudy, + color='yellow', alpha=0.5) + tdlh_axes.fill_between(self.time, -60, temp_diff, where=wvcloudy, + color='red', alpha=0.5) + tdlh_axes.plot_date([self.date, self.date], self.cfg['cloudiness_limits'], + 'g-', alpha=0.4) if self.thresholds: st = self.thresholds.get('threshold_very_cloudy', None) if st: - plt.plot_date([self.start, self.end], [st, st], 'r-', - markersize=2, markeredgewidth=0, alpha=0.3, - drawstyle="default") + tdlh_axes.plot_date([self.start, self.end], [st, st], 'r-', + markersize=2, markeredgewidth=0, alpha=0.3, + drawstyle="default") try: current_cloudiness = self.current_values['data']['sky_condition'] @@ -371,10 +372,10 @@ def plot_cloudiness_vs_time(self): except Exception: pass - plt.grid(which='major', color='k') - plt.yticks(range(-100, 100, 10)) - plt.ylim(self.cfg['cloudiness_limits']) - plt.xlim(self.lhstart, self.lhend) + tdlh_axes.grid(which='major', color='k') + tdlh_axes.set_yticks(range(-100, 100, 10)) + tdlh_axes.set_ylim(self.cfg['cloudiness_limits']) + tdlh_axes.set_xlim(self.lhstart, self.lhend) tdlh_axes.xaxis.set_major_locator(self.mins) tdlh_axes.xaxis.set_major_formatter(self.mins_fmt) tdlh_axes.xaxis.set_ticklabels([]) @@ -383,7 +384,7 @@ def plot_cloudiness_vs_time(self): def plot_windspeed_vs_time(self): """ Windspeed vs Time """ logger.debug('Plot Wind Speed vs. Time') - w_axes = plt.axes(self.plot_positions[2][0]) + w_axes = self.fig.add_axes(self.plot_positions[2][0]) wind_speed = self.table['wind_speed_KPH'] wind_mavg = moving_average(wind_speed, 9) @@ -412,14 +413,14 @@ def plot_windspeed_vs_time(self): if self.thresholds: st = self.thresholds.get('threshold_very_windy', None) if st: - plt.plot_date([self.start, self.end], [st, st], 'r-', - markersize=2, markeredgewidth=0, alpha=0.3, - drawstyle="default") + w_axes.plot_date([self.start, self.end], [st, st], 'r-', + markersize=2, markeredgewidth=0, alpha=0.3, + drawstyle="default") st = self.thresholds.get('threshold_very_gusty', None) if st: - plt.plot_date([self.start, self.end], [st, st], 'r-', - markersize=2, markeredgewidth=0, alpha=0.3, - drawstyle="default") + w_axes.plot_date([self.start, self.end], [st, st], 'r-', + markersize=2, markeredgewidth=0, alpha=0.3, + drawstyle="default") try: max_wind = max(wind_speed) @@ -432,12 +433,12 @@ def plot_windspeed_vs_time(self): ) except Exception: pass - plt.ylabel("Wind (km/h)") - plt.grid(which='major', color='k') -# plt.yticks(range(0, 200, 10)) + w_axes.set_ylabel("Wind (km/h)") + w_axes.grid(which='major', color='k') +# w_axes.yticks(range(0, 200, 10)) - plt.xlim(self.start, self.end) - plt.ylim(self.cfg['wind_limits']) + w_axes.set_xlim(self.start, self.end) + w_axes.set_ylim(self.cfg['wind_limits']) w_axes.xaxis.set_major_locator(self.hours) w_axes.xaxis.set_major_formatter(self.hours_fmt) w_axes.xaxis.set_ticklabels([]) @@ -446,7 +447,7 @@ def plot_windspeed_vs_time(self): w_axes.yaxis.set_minor_locator(MultipleLocator(10)) if self.today: - wlh_axes = plt.axes(self.plot_positions[2][1]) + wlh_axes = self.fig.add_axes(self.plot_positions[2][1]) wlh_axes.plot_date(self.time, wind_speed, 'ko', alpha=0.7, markersize=4, markeredgewidth=0, drawstyle="default") @@ -462,20 +463,20 @@ def plot_windspeed_vs_time(self): color='yellow', alpha=0.5) wlh_axes.fill_between(self.time, -5, wind_speed, where=wvwindy, color='red', alpha=0.5) - plt.plot_date([self.date, self.date], self.cfg['wind_limits'], - 'g-', alpha=0.4) + wlh_axes.plot_date([self.date, self.date], self.cfg['wind_limits'], + 'g-', alpha=0.4) if self.thresholds: st = self.thresholds.get('threshold_very_windy', None) if st: - plt.plot_date([self.start, self.end], [st, st], 'r-', - markersize=2, markeredgewidth=0, alpha=0.3, - drawstyle="default") + wlh_axes.plot_date([self.start, self.end], [st, st], 'r-', + markersize=2, markeredgewidth=0, alpha=0.3, + drawstyle="default") st = self.thresholds.get('threshold_very_gusty', None) if st: - plt.plot_date([self.start, self.end], [st, st], 'r-', - markersize=2, markeredgewidth=0, alpha=0.3, - drawstyle="default") + wlh_axes.plot_date([self.start, self.end], [st, st], 'r-', + markersize=2, markeredgewidth=0, alpha=0.3, + drawstyle="default") try: current_wind = self.current_values['data']['wind_speed_KPH'] @@ -489,10 +490,10 @@ def plot_windspeed_vs_time(self): ) except Exception: pass - plt.grid(which='major', color='k') -# plt.yticks(range(0, 200, 10)) - plt.xlim(self.lhstart, self.lhend) - plt.ylim(self.cfg['wind_limits']) + wlh_axes.grid(which='major', color='k') +# wlh_axes.yticks(range(0, 200, 10)) + wlh_axes.set_xlim(self.lhstart, self.lhend) + wlh_axes.set_ylim(self.cfg['wind_limits']) wlh_axes.xaxis.set_major_locator(self.mins) wlh_axes.xaxis.set_major_formatter(self.mins_fmt) wlh_axes.xaxis.set_ticklabels([]) @@ -505,7 +506,7 @@ def plot_rain_freq_vs_time(self): """ Rain Frequency vs Time """ logger.debug('Plot Rain Frequency vs. Time') - rf_axes = plt.axes(self.plot_positions[3][0]) + rf_axes = self.fig.add_axes(self.plot_positions[3][0]) rf_value = self.table['rain_frequency'] rain_condition = self.table['rain_condition'] @@ -527,20 +528,20 @@ def plot_rain_freq_vs_time(self): if self.thresholds: st = self.thresholds.get('threshold_wet', None) if st: - plt.plot_date([self.start, self.end], [st, st], 'r-', - markersize=2, markeredgewidth=0, alpha=0.3, - drawstyle="default") - - plt.ylabel("Rain Sensor") - plt.grid(which='major', color='k') - plt.ylim(self.cfg['rain_limits']) - plt.xlim(self.start, self.end) + rf_axes.plot_date([self.start, self.end], [st, st], 'r-', + markersize=2, markeredgewidth=0, alpha=0.3, + drawstyle="default") + + rf_axes.set_ylabel("Rain Sensor") + rf_axes.grid(which='major', color='k') + rf_axes.set_ylim(self.cfg['rain_limits']) + rf_axes.set_xlim(self.start, self.end) rf_axes.xaxis.set_major_locator(self.hours) rf_axes.xaxis.set_major_formatter(self.hours_fmt) rf_axes.xaxis.set_ticklabels([]) if self.today: - rflh_axes = plt.axes(self.plot_positions[3][1]) + rflh_axes = self.fig.add_axes(self.plot_positions[3][1]) rflh_axes.plot_date(self.time, rf_value, 'ko-', label='Rain', markersize=4, markeredgewidth=0, drawstyle="default") @@ -550,12 +551,12 @@ def plot_rain_freq_vs_time(self): color='orange', alpha=0.5) rflh_axes.fill_between(self.time, 0, rf_value, where=wrain, color='red', alpha=0.5) - plt.plot_date([self.date, self.date], self.cfg['rain_limits'], - 'g-', alpha=0.4) + rflh_axes.plot_date([self.date, self.date], self.cfg['rain_limits'], + 'g-', alpha=0.4) if st: - plt.plot_date([self.start, self.end], [st, st], 'r-', - markersize=2, markeredgewidth=0, alpha=0.3, - drawstyle="default") + rflh_axes.plot_date([self.start, self.end], [st, st], 'r-', + markersize=2, markeredgewidth=0, alpha=0.3, + drawstyle="default") try: current_rain = self.current_values['data']['rain_condition'] @@ -569,9 +570,9 @@ def plot_rain_freq_vs_time(self): ) except Exception: pass - plt.grid(which='major', color='k') - plt.ylim(self.cfg['rain_limits']) - plt.xlim(self.lhstart, self.lhend) + rflh_axes.grid(which='major', color='k') + rflh_axes.set_ylim(self.cfg['rain_limits']) + rflh_axes.set_xlim(self.lhstart, self.lhend) rflh_axes.xaxis.set_major_locator(self.mins) rflh_axes.xaxis.set_major_formatter(self.mins_fmt) rflh_axes.xaxis.set_ticklabels([]) @@ -581,7 +582,7 @@ def plot_safety_vs_time(self): """ Plot Safety Values """ logger.debug('Plot Safe/Unsafe vs. Time') - safe_axes = plt.axes(self.plot_positions[4][0]) + safe_axes = self.fig.add_axes(self.plot_positions[4][0]) safe_value = [int(x) for x in self.table['safe']] @@ -594,18 +595,18 @@ def plot_safety_vs_time(self): safe_axes.fill_between(self.time, -1, safe_value, where=(~self.table['safe']), color='red', alpha=0.5) - plt.ylabel("Safe") - plt.xlim(self.start, self.end) - plt.ylim(-0.1, 1.1) - plt.yticks([0, 1]) - plt.grid(which='major', color='k') + safe_axes.set_ylabel("Safe") + safe_axes.set_xlim(self.start, self.end) + safe_axes.set_ylim(-0.1, 1.1) + safe_axes.set_yticks([0, 1]) + safe_axes.grid(which='major', color='k') safe_axes.xaxis.set_major_locator(self.hours) safe_axes.xaxis.set_major_formatter(self.hours_fmt) safe_axes.xaxis.set_ticklabels([]) safe_axes.yaxis.set_ticklabels([]) if self.today: - safelh_axes = plt.axes(self.plot_positions[4][1]) + safelh_axes = self.fig.add_axes(self.plot_positions[4][1]) safelh_axes.plot_date(self.time, safe_value, 'ko-', markersize=4, markeredgewidth=0, drawstyle="default") @@ -615,8 +616,8 @@ def plot_safety_vs_time(self): safelh_axes.fill_between(self.time, -1, safe_value, where=(~self.table['safe']), color='red', alpha=0.5) - plt.plot_date([self.date, self.date], [-0.1, 1.1], - 'g-', alpha=0.4) + safelh_axes.plot_date([self.date, self.date], [-0.1, 1.1], + 'g-', alpha=0.4) try: safe = self.current_values['data']['safe'] current_safe = {True: 'Safe', False: 'Unsafe'}[safe] @@ -630,10 +631,10 @@ def plot_safety_vs_time(self): ) except Exception: pass - plt.ylim(-0.1, 1.1) - plt.yticks([0, 1]) - plt.grid(which='major', color='k') - plt.xlim(self.lhstart, self.lhend) + safelh_axes.set_ylim(-0.1, 1.1) + safelh_axes.set_yticks([0, 1]) + safelh_axes.grid(which='major', color='k') + safelh_axes.set_xlim(self.lhstart, self.lhend) safelh_axes.xaxis.set_major_locator(self.mins) safelh_axes.xaxis.set_major_formatter(self.mins_fmt) safelh_axes.xaxis.set_ticklabels([]) @@ -643,15 +644,15 @@ def plot_pwm_vs_time(self): """ Plot Heater values """ logger.debug('Plot PWM Value vs. Time') - pwm_axes = plt.axes(self.plot_positions[5][0]) - plt.ylabel("Heater (%)") - plt.ylim(self.cfg['pwm_limits']) - plt.yticks([0, 25, 50, 75, 100]) - plt.xlim(self.start, self.end) - plt.grid(which='major', color='k') + pwm_axes = self.fig.add_axes(self.plot_positions[5][0]) + pwm_axes.set_ylabel("Heater (%)") + pwm_axes.set_ylim(self.cfg['pwm_limits']) + pwm_axes.set_yticks([0, 25, 50, 75, 100]) + pwm_axes.set_xlim(self.start, self.end) + pwm_axes.grid(which='major', color='k') rst_axes = pwm_axes.twinx() - plt.ylim(-1, 21) - plt.xlim(self.start, self.end) + rst_axes.set_ylim(-1, 21) + rst_axes.set_xlim(self.start, self.end) pwm_value = self.table['pwm_value'] rst_delta = self.table['rain_sensor_temp_C'] - self.table['ambient_temp_C'] @@ -673,14 +674,14 @@ def plot_pwm_vs_time(self): pwm_axes.legend(loc='best') if self.today: - pwmlh_axes = plt.axes(self.plot_positions[5][1]) - plt.ylim(self.cfg['pwm_limits']) - plt.yticks([0, 25, 50, 75, 100]) - plt.xlim(self.lhstart, self.lhend) - plt.grid(which='major', color='k') + pwmlh_axes = self.fig.add_axes(self.plot_positions[5][1]) + pwmlh_axes.set_ylim(self.cfg['pwm_limits']) + pwmlh_axes.set_yticks([0, 25, 50, 75, 100]) + pwmlh_axes.set_xlim(self.lhstart, self.lhend) + pwmlh_axes.grid(which='major', color='k') rstlh_axes = pwmlh_axes.twinx() - plt.ylim(-1, 21) - plt.xlim(self.lhstart, self.lhend) + rstlh_axes.set_ylim(-1, 21) + rstlh_axes.set_xlim(self.lhstart, self.lhend) rstlh_axes.plot_date(self.time, rst_delta, 'ro-', alpha=0.5, label='RST Delta (C)', markersize=4, markeredgewidth=0, @@ -710,10 +711,9 @@ def save_plot(self, plot_filename=None): plot_filename = os.path.abspath(plot_filename) plot_dir = os.path.dirname(plot_filename) - if not os.path.exists(plot_dir): - os.makedirs(plot_dir) + os.makedirs(plot_dir, exist_ok=True) - logger.info('Saving weather plot: {}'.format(plot_filename)) + logger.info(f'Saving weather plot: {plot_filename}') self.fig.savefig( plot_filename, dpi=self.dpi, From eeee8fe0d38cb42c4e1da0715cb51a181cad0834 Mon Sep 17 00:00:00 2001 From: Wilfred Tyler Gee Date: Mon, 19 Aug 2019 15:49:14 +1000 Subject: [PATCH 6/6] Minor bug fixes --- scripts/plot_weather.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/scripts/plot_weather.py b/scripts/plot_weather.py index a9eb6f346..15084c4a7 100755 --- a/scripts/plot_weather.py +++ b/scripts/plot_weather.py @@ -63,11 +63,11 @@ def __init__(self, date_string=None, data_file=None, *args, **kwargs): else: self.today = False - self.date = date_parser('{date_string} 23:59:59') + self.date = date_parser(f'{date_string} 23:59:59') self.date_string = date_string self.start = dt(self.date.year, self.date.month, self.date.day, 0, 0, 0, 0) self.end = dt(self.date.year, self.date.month, self.date.day, 23, 59, 59, 0) - logger.info(f'Creating weather plotter for {date_string}') + logger.info(f'Creating weather plotter for {self.date_string}') self.location = create_location_from_config(config) self.twilights = self.get_twilights() @@ -94,12 +94,12 @@ def make_plot(self, output_file=None): # Plot a day's weather # ------------------------------------------------------------------------- start_time = f'{self.start:{self.date_format}}' - start_hour = f'{self.lhstart:{self.date_format}}' end_time = f'{self.end:{self.date_format}}' - end_hour = f'{self.lhend:{self.date_format}}' logger.debug(f'Setting up plot for time range: {start_time} to {end_time}') if self.today: + start_hour = f'{self.lhstart:{self.date_format}}' + end_hour = f'{self.lhend:{self.date_format}}' logger.debug(f'Will generate last hour plot: {start_hour} to {end_hour}') self.dpi = self.kwargs.get('dpi', 72)