db_weather.py

'''\
Weather Database Table classes.

classes:
    dbWeatherTable (dbTable)
    dbWeatherStationTable (dbWeatherTable)
    dbLocalClimateDataTable (dbWeatherTable)

dbWeatherStationTable methods:
    create_table    ()
    destroy_table   ()
    list_to_file    ()

dbLocalClimateDataTable methods:
    create_table        ()
    destroy_table       ()
    load_records        (tab_filename)
    list_to_file        ()
    list_report_types   ()

    chart_month_temps   (month, year=2017)
    chart_month_precip  (month, year=2017)

    chart_year_temps    (year)
    chart_year_precip   (year)

    chart_decade_temps  (year0)
    chart_decade_precip (year0)

    chart_months_temps      (month, year_0, year_1)
    chart_months_temps_hi   (month, year_0, year_1)
    chart_months_temps_lo   (month, year_0, year_1)

    chart_temp_min_max  ()
    chart_pressure      ()
    chart_rel_hum       ()

Developer@Sonnack.com
June 2017
'''
####################################################################################################
from __future__ import print_function
from sys import argvstdoutstderr
from os import path
from datetime import datetimedelta
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.ticker import MultipleLocatorFormatStrFormatter
from matplotlib.dates import DayLocatorDateFormatter
from files import get_file_csv
from dbsql import dbTable
####################################################################################################

BasePath = r'C:\CJS\prj\Python\weather'
DataPath = r'C:\CJS\prj\Python\weather\data'
ImgsPath = r'C:\CJS\prj\Python\weather\imgs'


##~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~##
class dbWeatherTable (dbTable):
    '''Base class for Weather tables.'''
    dbName = 'weather.sqlite'
    dataPath = DataPath
    imgsPath = ImgsPath

    def __init__(selftab_nametab_cols):
        dbTable.__init__(selfpath.join(self.dataPath,self.dbName), tab_nametab_cols)
        self.col_headers = [h[1:-1if h.startswith('"'else h for h in self.col_names]


##~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~##
class dbWeatherStationTable (dbWeatherTable):
    tabName = 'stations'
    tabCols = ['"station"   TEXT NOT NULL UNIQUE'
              ,'"name"      TEXT NOT NULL UNIQUE'
              ,'"elevation" REAL DEFAULT 0.0'
              ,'"latitude"  REAL DEFAULT 0.0'
              ,'"longitude" REAL DEFAULT 0.0'
              ]
    tabData = []
    tabFilename = 'lcd_2010-2017.csv'

    def __init__(self):
        dbWeatherTable.__init__(selfself.tabNameself.tabCols)

    def create_table (self):
        self.Log.info('Create Stations Dataset')
        self.db_create()
        # Load data...
        data = get_file_csv(path.join(self.dataPath,self.tabFilename))
        data = [d[0:5for d in data[1]]
        # Get unique station info...
        stations = {}
        for d in data: stations[d[0]] = d
        self.tabData = list(stations.values())
        self.db_insert_many(self.tabData)
        for r in self.tabData:
            self.Log.trace(r)

    def destroy_table (self):
        dbWeatherTable.destroy_table(self'Drop Stations Dataset')

    def list_to_file (selffname='stations.out'):
        self.Log.debug('db[%s] ListToFile' % self.name)
        h1 = 'Station-Id   Station-Name                                       Elev      Lat.      Long.\n'
        h2 = '============ ================================================== ======= = ======= = =======\n'
        fmt = '%-12s %-50s %+7.2f : %+7.2f x %+7.2f\n'
        sql = 'SELECT * FROM "%s" ORDER BY name'
        rs = self.db_query(sql % self.name)
        fn = path.join(BasePathfname)
        fp = open(fn'w')
        try:
            fp.write(h1)
            fp.write(h2)
            for r in rs: fp.write(fmt % r)
        except:
            raise
        finally:
            fp.close()
        self.Log.info('wrote: %s' % fn)


##~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~##
class dbLocalClimateDataTable (dbWeatherTable):
    tabName = 'local_climate_data'
    tabCols = ['"station" TEXT DEFAULT NULL'
              ,'"date" TEXT DEFAULT NULL'
              ,'"time" TEXT DEFAULT NULL'
              ,'"report_type" TEXT DEFAULT NULL'
              ,'"hr_skycondits" TEXT DEFAULT NULL'
              ,'"hr_visibility" REAL DEFAULT NULL'
              ,'"hr_curr_weather_type" TEXT DEFAULT NULL'
              ,'"hr_drybulb_tempf" INTEGER DEFAULT NULL'
              ,'"hr_drybulb_tempc" REAL DEFAULT NULL'
              ,'"hr_wetbulb_tempf" INTEGER DEFAULT NULL'
              ,'"hr_wetbulb_tempc" REAL DEFAULT NULL'
              ,'"hr_dewpoint_tempf" INTEGER DEFAULT NULL'
              ,'"hr_dewpoint_tempc" REAL DEFAULT NULL'
              ,'"hr_rel_hum" INTEGER DEFAULT NULL'
              ,'"hr_wind_spd" REAL DEFAULT NULL'
              ,'"hr_wind_dir" REAL DEFAULT NULL'
              ,'"hr_wind_gust_spd" REAL DEFAULT NULL'
              ,'"hr_station_press" REAL DEFAULT NULL'
              ,'"hr_press_tendency" INTEGER DEFAULT NULL'
              ,'"hr_press_change" REAL DEFAULT NULL'
              ,'"hr_sealevel_press" REAL DEFAULT NULL'
              ,'"hr_precip" REAL DEFAULT NULL'
              ,'"hr_altimeter_setting" REAL DEFAULT NULL'
              ,'"day_max_drybulb_temp" INTEGER DEFAULT NULL'
              ,'"day_min_drybulb_temp" INTEGER DEFAULT NULL'
              ,'"day_avg_drybulb_temp" INTEGER DEFAULT NULL'
              ,'"day_dept_from_norm_avg_temp" REAL DEFAULT NULL'
              ,'"day_avg_rel_hum" REAL DEFAULT NULL'
              ,'"day_avg_dewpoint_temp" REAL DEFAULT NULL'
              ,'"day_avg_wetbulb_temp" REAL DEFAULT NULL'
              ,'"day_heat_deg_days" INTEGER DEFAULT NULL'
              ,'"day_cool_deg_days" INTEGER DEFAULT NULL'
              ,'"day_sunrise" TEXT DEFAULT NULL'
              ,'"day_sunset" TEXT DEFAULT NULL'
              ,'"day_weather" TEXT DEFAULT NULL'
              ,'"day_precip" REAL DEFAULT NULL'
              ,'"day_snowfall" REAL DEFAULT NULL'
              ,'"day_snowdepth" TEXT DEFAULT NULL'
              ,'"day_avg_station_press" REAL DEFAULT NULL'
              ,'"day_avg_sealevel_press" REAL DEFAULT NULL'
              ,'"day_avg_wind_spd" REAL DEFAULT NULL'
              ,'"day_peak_wind_spd" REAL DEFAULT NULL'
              ,'"peak_wind_dir" REAL DEFAULT NULL'
              ,'"day_sus_wind_spd" REAL DEFAULT NULL'
              ,'"day_sus_wind_dir" REAL DEFAULT NULL'
              ,'"mon_max_temp" REAL DEFAULT NULL'
              ,'"mon_min_temp" REAL DEFAULT NULL'
              ,'"mon_mean_temp" REAL DEFAULT NULL'
              ,'"mon_avg_rh" TEXT DEFAULT NULL'
              ,'"mon_dewpoint_temp" TEXT DEFAULT NULL'
              ,'"mon_wetbulb_temp" TEXT DEFAULT NULL'
              ,'"mon_avg_heat_deg_days" INTEGER DEFAULT NULL'
              ,'"mon_avg_cool_deg_days" INTEGER DEFAULT NULL'
              ,'"mon_station_press" REAL DEFAULT NULL'
              ,'"mon_sealevel_press" REAL DEFAULT NULL'
              ,'"mon_avg_wind_spd" REAL DEFAULT NULL'
              ,'"mon_tot_snowfall" REAL DEFAULT NULL'
              ,'"mon_dept_from_norm_max_temp" REAL DEFAULT NULL'
              ,'"mon_dept_from_norm_min_temp" REAL DEFAULT NULL'
              ,'"mon_dept_from_norm_avg_temp" REAL DEFAULT NULL'
              ,'"mon_dept_from_norm_precip" REAL DEFAULT NULL'
              ,'"mon_tot_liquid_precip" REAL DEFAULT NULL'
              ,'"mon_greatest_precip" REAL DEFAULT NULL'
              ,'"mon_greatest_precipdate" REAL DEFAULT NULL'
              ,'"mon_greatest_snowfall" REAL DEFAULT NULL'
              ,'"mon_greatest_snowfalldate" TEXT DEFAULT NULL'
              ,'"mon_greatest_snowdepth" REAL DEFAULT NULL'
              ,'"mon_greatest_snowdepthdate" TEXT DEFAULT NULL'
              ,'"mon_dayswith_gt90_temp" REAL DEFAULT NULL'
              ,'"mon_dayswith_lt32_temp" REAL DEFAULT NULL'
              ,'"mon_dayswith_gt32_temp" REAL DEFAULT NULL'
              ,'"mon_dayswith_lt0_temp" REAL DEFAULT NULL'
              ,'"mon_dayswith_gt001_precip" REAL DEFAULT NULL'
              ,'"mon_dayswith_gt010_precip" REAL DEFAULT NULL'
              ,'"mon_dayswith_gt1_snow" REAL DEFAULT NULL'
              ,'"mon_max_sealevel_pressvalue" REAL DEFAULT NULL'
              ,'"mon_max_sealevel_pressdate" TEXT DEFAULT NULL'
              ,'"mon_max_sealevel_presstime" TEXT DEFAULT NULL'
              ,'"mon_min_sealevel_pressvalue" REAL DEFAULT NULL'
              ,'"mon_min_sealevel_pressdate" TEXT DEFAULT NULL'
              ,'"mon_min_sealevel_presstime" TEXT DEFAULT NULL'
              ,'"mon_tot_heat_deg_days" INTEGER DEFAULT NULL'
              ,'"mon_tot_cool_deg_days" INTEGER DEFAULT NULL'
              ,'"mon_dept_from_norm_heat_dd" TEXT DEFAULT NULL'
              ,'"mon_dept_from_norm_cool_dd" TEXT DEFAULT NULL'
              ,'"mon_tot_sea_todate_heat_dd" TEXT DEFAULT NULL'
              ,'"mon_tot_sea_todate_cool_dd" TEXT DEFAULT NULL'
              ]
    tabData = []
    tabFilename = 'lcd_2000-2017.csv'

    def __init__(self):
        dbWeatherTable.__init__(selfself.tabNameself.tabCols)
        self.snow_fall_factor = 4.0

    def create_table (self):
        self.Log.info('Create LCD Dataset')
        self.db_create()
        self.load_records(self.tabFilename)

    def destroy_table (self):
        dbWeatherTable.destroy_table(self'Drop LCD Dataset')

    def load_records (selftab_filename):
        data = get_file_csv(path.join(self.dataPathtab_filename))
        self.tabData = [d[0:1]+[d[5][0:10],d[5][11:16]]+d[6:for d in data[1]]
        self.db_insert_many(self.tabData)

    def list_to_file (selffname='lcd-data.out'):
        self.Log.debug('db[%s] ListToFile' % self.name)
        h1 = 'Date       #Rs   Max/Low  Rel.H  W.Spd  Press\n'
        h2 = '==========-===-  ===-===  =====  =====  =====\n'
        fmt = '%s[%3d]  %+3d/%+3d  %5.2f  %4.1f  %5.2f\n'
        sql = Queries['list']
        rs = self.db_query(sql % self.name)
        fn = path.join(BasePathfname)
        fp = open(fn'w')
        try:
            fp.write(h1)
            fp.write(h2)
            for r in rs: fp.write(fmt % r)
        except: raise
        finally: fp.close()
        self.Log.info('wrote: %s' % fn)

    def list_report_types (selffname='report-types.out'):
        self.Log.debug('db[%s] List(report_type)' % self.name)
        h1 = 'Report Type  Rcds\n'
        h2 = '===========  ====\n'
        fmt = '%-11s  %4d\n'
        sql = Queries['report-types']
        rs = self.db_query(sql % self.name)
        fn = path.join(BasePathfname)
        fp = open(fn'w')
        try:
            fp.write(h1)
            fp.write(h2)
            for r in rs: fp.write(fmt % r)
        except: raise
        finally: fp.close()
        self.Log.info('wrote: %s' % fn)

    ##__________________________________________________________________________________##
    def chart_month_temps (selfmonthyear=2017fname='temps_%04d-%02d.png'):
        self.Log.debug('db[%s] Chart(month.temps)' % self.name)
        fn = path.join(self.imgsPath,fname % (year,month))
        ps = ['SOD''%04d'%year'%02d'%month]
        # Query...
        sql = Queries['month.temps.c']
        rs = self.db_query(sql % self.nameps)
        rs = [r for r in rs if isinstance(r[3],intand isinstance(r[4],int)]
        if not rs: rs = [(year,month,15,+1,-1)]
        # Munch on the query results...
        dtYs,dtMs,dtDs,tmin,tmax = zip(*rs)
        dts = [date(dy,dm,ddfor dy,dm,dd in zip(dtYs,dtMs,dtDs)]
        tmax_max = max(tmax)
        tmax_min = min(tmax)
        tmin_maxmax(tmin)
        tmin_minmin(tmin)
        # Chart it...
        ttl = 'Hi/Lo Temps %s'
        ylab = 'Temperature'
        figax = self.__chart_month('temps'month,year,0title=ttlylab=ylab)
        ax.axhspan(tmax_min,tmax_maxcolor='#ff0000' , lw=1.0alpha=0.10zorder=7)
        ax.axhspan(tmin_min,tmin_maxcolor='#0000ff' , lw=1.0alpha=0.10zorder=6)
        ax.fill_between(dts,tmin,tmaxcolor='#000000'alpha=0.10zorder=8)
        ax.plot(dts,tmaxcolor='#bf3f00'lw=1.5alpha=1.00zorder=9marker='*',ms=5)
        ax.plot(dts,tmincolor='#003fbf'lw=1.5alpha=1.00zorder=9marker='*',ms=5)
        # Save chart...
        self.__chart_month_save(figfn)
        return rs

    def chart_month_precip (selfmonthyear=2017fname='precip_%04d-%02d.png'):
        self.Log.debug('db[%s] Chart(month.precip)' % self.name)
        fn = path.join(self.imgsPath,fname % (year,month))
        ps = ['SOD''%04d'%year'%02d'%month]
        # Query...
        sql = Queries['month.precip.c']
        rs = self.db_query(sql % self.nameps)
        # Munch on the query results...
        rain = [(date(r[0],r[1],r[2]),r[3]) for r in rs if isinstance(r[3],float)]
        snow = [(date(r[0],r[1],r[2]),r[4]) for r in rs if isinstance(r[4],float)]
        if rain: rain_dtsrain_vals = zip(*rain)
        if snow: snow_dtssnow_vals = zip(*snow)
        if snow: snow_vals = [v/self.snow_fall_factor for v in snow_vals]
        # Chart it...
        ttl = 'Rain & Snow %s'
        ylab = 'Precip (inches)'
        figax = self.__chart_month('precip'month,year,0title=ttlylab=ylab)
        if rain: ax.fill_between(rain_dts,rain_vals,0color='#007fff'alpha=0.95zorder=9)
        if snow: ax.fill_between(snow_dts,snow_vals,0color='#3f3f3f'alpha=0.75zorder=8)
        # Save chart...
        self.__chart_month_save(figfn)
        return rs

    ##__________________________________________________________________________________##
    def chart_months_temps (selfmonthyear_0year_1fname='temps_%02d-%04d-%04d.png'):
        self.Log.debug('db[%s] Chart(months.temps)' % self.name)
        fn = path.join(self.imgsPath,fname % (month,year_0,year_1))
        ps = ['SOD''%02d'%month'%4d'%year_0'%4d'%year_1]
        # Query...
        sql = Queries['months.temps.c']
        rs = self.db_query(sql % self.nameps)
        rs = [r for r in rs if isinstance(r[3],intand isinstance(r[4],int)]
        if not rs: self.Log.warn('No Data!'); return []
        # Munch on the query results...
        years = {}
        for dtY,dtM,dtD,tmin,tmax in rs:
            if dtY not in years: years[dtY] = []
            years[dtY].append((dtM,dtD,tmin,tmax))
        # Chart it...
        ttl = 'Hi-Lo Temps %s-%s'
        ylab = 'Temperature'
        figax = self.__chart_month('temps'month,year_0,year_1title=ttlylab=ylab)
        for ix,yr in enumerate(reversed(years.keys())):
            series = years[yr]
            dts = [date(year_0,d[0],d[1]) for d in series]
            tmin = [d[2for d in series]
            tmax = [d[3for d in series]
            ax.fill_between(dts,tmax,tminlabel=str(yr), color=(0.05*ix0.05*ix1.0), alpha=0.50zorder=9+ix)
        # Save chart...
        self.__chart_month_save(figfn)
        return rs

    def chart_months_temps_hi (selfmonthyear_0year_1fname='temps_hi_%02d-%04d-%04d.png'):
        self.Log.debug('db[%s] Chart(months.temps.hi)' % self.name)
        fn = path.join(self.imgsPath,fname % (month,year_0,year_1))
        ps = ['SOD''%02d'%month'%4d'%year_0'%4d'%year_1]
        # Query...
        sql = Queries['months.temps.hi.c']
        rs = self.db_query(sql % self.nameps)
        rs = [r for r in rs if isinstance(r[3],int)]
        if not rs: self.Log.warn('No Data!'); return []
        # Munch on the query results...
        years = {}
        for dtY,dtM,dtD,tmax in rs:
            if dtY not in years: years[dtY] = []
            years[dtY].append((dtM,dtD,tmax))
        # Chart it...
        ttl = 'Hi Temps %s-%s'
        ylab = 'Temperature'
        figax = self.__chart_month('temps'month,year_0,year_1title=ttlylab=ylab)
        for ix,yr in enumerate(reversed(years.keys())):
            series = years[yr]
            dts = [date(year_0,d[0],d[1]) for d in series]
            tmax = [d[2for d in series]
            ax.plot(dts,tmaxlabel=str(yr), color=(1.00.05*ix0.05*ix), alpha=0.75zorder=9)
        # Save chart...
        self.__chart_month_save(figfn)
        return rs

    def chart_months_temps_lo (selfmonthyear_0year_1fname='temps_lo_%02d-%04d-%04d.png'):
        self.Log.debug('db[%s] Chart(months.temps.lo)' % self.name)
        fn = path.join(self.imgsPath,fname % (month,year_0,year_1))
        ps = ['SOD''%02d'%month'%4d'%year_0'%4d'%year_1]
        # Query...
        sql = Queries['months.temps.lo.c']
        rs = self.db_query(sql % self.nameps)
        rs = [r for r in rs if isinstance(r[3],int)]
        if not rs: self.Log.warn('No Data!'); return []
        # Munch on the query results...
        years = {}
        for dtY,dtM,dtD,tmin in rs:
            if dtY not in years: years[dtY] = []
            years[dtY].append((dtM,dtD,tmin))
        # Chart it...
        ttl = 'Lo Temps %s-%s'
        ylab = 'Temperature'
        figax = self.__chart_month('temps'month,year_0,year_1title=ttlylab=ylab)
        for ix,yr in enumerate(reversed(years.keys())):
            series = years[yr]
            dts = [date(year_0,d[0],d[1]) for d in series]
            tmin = [d[2for d in series]
            ax.plot(dts,tminlabel=str(yr), color=(0.05*ix0.05*ix1.0), alpha=0.75zorder=9)
        # Save chart...
        self.__chart_month_save(figfn)
        return rs

    ##__________________________________________________________________________________##
    def chart_year_temps (selfyearfname='temps_%04d.png'):
        self.Log.debug('db[%s] Chart(year.temps)' % self.name)
        fn = path.join(self.imgsPath,fname % year)
        ps = ['SOD''%4d'%year]
        # Query...
        sql = Queries['year.temps.c']
        rs = self.db_query(sql % self.nameps)
        rs = [r for r in rs if isinstance(r[3],intand isinstance(r[4],int)]
        if not rs: rs = [(year,1,1,+1,-1)]
        # Munch on the query results...
        dts = [date(r[0],r[1],r[2]) for r in rs]
        tmin = [r[3for r in rs]
        tmax = [r[4for r in rs]
        # Chart it...
        ttl = 'Hi/Lo Temps %04d'
        ylab = 'Temperature'
        figax = self.__chart_year('temps'yeartitle=ttlylab=ylab)
        ax.fill_between(dts,tmax,tmincolor='#007fbf'alpha=0.70zorder=9)
        # Save chart...
        self.__chart_year_save(figfn)
        return rs

    def chart_year_precip (selfyearfname='precip_%04d.png'):
        self.Log.debug('db[%s] Chart(year.precip)' % self.name)
        fn = path.join(self.imgsPath,fname % year)
        ps = ['SOD''%4d'%year]
        # Query...
        sql = Queries['year.precip.c']
        rs = self.db_query(sql % self.nameps)
        # Munch on the query results...
        rain = [(date(r[0],r[1],r[2]),r[3]) for r in rs if isinstance(r[3],float)]
        snow = [(date(r[0],r[1],r[2]),r[4]) for r in rs if isinstance(r[4],float)]
        if rain: rain_dtsrain_vals = zip(*rain)
        if snow: snow_dtssnow_vals = zip(*snow)
        if snow: snow_vals = [v/self.snow_fall_factor for v in snow_vals]
        # Chart it...
        ttl = 'Rain & Snow %04d'
        ylab = 'Precip (inches)'
        figax = self.__chart_year('precip'yeartitle=ttlylab=ylab)
        if rain: ax.fill_between(rain_dts,rain_vals,0color='#007fff'alpha=0.95zorder=9)
        if snow: ax.fill_between(snow_dts,snow_vals,0color='#3f3f3f'alpha=0.75zorder=8)
        # Save chart...
        self.__chart_year_save(figfn)
        return rs

    ##__________________________________________________________________________________##
    def chart_decade_temps (selfyear0fname='temps_%04d-%04d.png'):
        self.Log.debug('db[%s] Chart(decade.temps)' % self.name)
        fn = path.join(self.imgsPath,fname % (year0,year0+9))
        ps = ['SOD''%4d'%year0'%4d'%(year0+10)]
        # Query...
        sql = Queries['decade.temps.c']
        rs = self.db_query(sql % self.nameps)
        rs = [r for r in rs if isinstance(r[3],intand isinstance(r[4],int)]
        if not rs: self.Log.warn('No Data!'); return []
        # Munch on the query results...
        dts = [date(r[0],r[1],r[2]) for r in rs]
        tmin = [r[3for r in rs]
        tmax = [r[4for r in rs]
        # Chart it...
        ttl = 'Hi/Lo Temps %04d-%04d'
        ylab = 'Temperature'
        figax = self.__chart_decade('temps'year0title=ttlylab=ylab)
        ax.plot(dts,tmaxcolor='#ff0000'alpha=0.50zorder=9)
        ax.plot(dts,tmincolor='#0000ff'alpha=0.50zorder=9)
        # Save chart...
        self.__chart_decade_save(figfn)
        return rs

    def chart_decade_precip (selfyear0fname='precip_%04d-%04d.png'):
        self.Log.debug('db[%s] Chart(decade.precip)' % self.name)
        fn = path.join(self.imgsPath,fname % (year0,year0+9))
        ps = ['SOD''%4d'%year0'%4d'%(year0+10)]
        # Query...
        sql = Queries['decade.precip.c']
        rs = self.db_query(sql % self.nameps)
        # Munch on the query results...
        rain = [(date(r[0],r[1],r[2]),r[3]) for r in rs if isinstance(r[3],float)]
        snow = [(date(r[0],r[1],r[2]),r[4]) for r in rs if isinstance(r[4],float)]
        if rain: rain_dtsrain_vals = zip(*rain)
        if snow: snow_dtssnow_vals = zip(*snow)
        if snow: snow_vals = [v/self.snow_fall_factor for v in snow_vals]
        # Chart it...
        ttl = 'Rain & Snow %d-%d'
        ylab = 'Precip (inches)'
        figax = self.__chart_decade('precip'year0title=ttlylab=ylab)
        if rain: ax.fill_between(rain_dts,rain_vals,0color='#007fff'alpha=0.95zorder=9)
        if snow: ax.fill_between(snow_dts,snow_vals,0color='#3f3f3f'alpha=0.75zorder=8)
        # Save chart...
        self.__chart_decade_save(figfn)
        return rs

    ##__________________________________________________________________________________##
    def __chart_month (selfctypemonth,year0,year1title=None,axtitle=Nonexlab=None,ylab=Noneytop=+3.0):
        figax = plt.subplots()
        if title:
            # assuming: 'Blah blah %s [-%4d]' % year0$[%b %d],year1
            dt = date(year0,month,1)
            ttl = (title % (dt.strftime('%B %Y'),year1)) if year1 else (title % dt.strftime('%B %Y'))
            fig.suptitle(ttlfontsize=13)
        if axtitle:
            ax.set_title(axtitlefontsize=12)
        ax.minorticks_on()
        ax.tick_params(which='major'axis='both'direction='out')
        ax.tick_params(which='minor'axis='both'direction='out')
        # Axes...
        self.__chart_month_x_axis(axmonth,year0xlab=xlab)
        if ctype == 'temps': self.__chart_y_axis_temperature(axylab=ylab)
        if ctype == 'precip': self.__chart_y_axis_precip(axylab=ylabytop=ytop)
        return (figax)

    def __chart_year (selfctypeyeartitle=Noneaxtitle=Nonexlab=Noneylab=Noneytop=+5.0):
        figax = plt.subplots()
        if title:
            # assuming: 'Blah blah %4d' % year
            ttl = title % year
            fig.suptitle(ttlfontsize=13)
        if axtitle:
            ax.set_title(axtitlefontsize=12)
        ax.minorticks_on()
        ax.tick_params(which='major'axis='both'direction='out')
        ax.tick_params(which='minor'axis='both'direction='out')
        # Axes...
        self.__chart_year_x_axis(axyearxlab=xlab)
        if ctype == 'temps': self.__chart_y_axis_temperature(axylab=ylab)
        if ctype == 'precip': self.__chart_y_axis_precip(axylab=ylabytop=ytop)
        return (figax)

    def __chart_decade (selfctypeyear0title=Noneaxtitle=Nonexlab=Noneylab=Noneytop=+3.0):
        figax = plt.subplots()
        if title:
            # assuming: 'Blah blah %4d-%4d' % (year0,year9)
            ttl = title % (year0year0+9)
            fig.suptitle(ttlfontsize=13)
        if axtitle:
            ax.set_title(axtitlefontsize=12)
        ax.minorticks_on()
        ax.tick_params(which='major'axis='both'direction='out')
        ax.tick_params(which='minor'axis='both'direction='out')
        # Axes...
        self.__chart_decade_x_axis(axyear0xlab=xlab)
        if ctype == 'temps': self.__chart_y_axis_temperature(axylab=ylab)
        if ctype == 'precip': self.__chart_y_axis_precip(axylab=ylabytop=ytop)
        return (figax)

    ##__________________________________________________________________________________##
    def __chart_month_x_axis (selfaxmonth,yearxlab=None):
        xmin = date(year,month,1)
        xmax = (date(year,month+1,1if month < 12 else date(year+1,1,1)) - timedelta(1)
        # X-Axis...
        if xlab: ax.set_xlabel(xlabfontsize=11)
        ax.xaxis_date()
        ax.set_xlim(xminxmax)
        ax.xaxis.set_major_locator(DayLocator(bymonthday=range(1,32,7) ))
        ax.xaxis.set_minor_locator(DayLocator(interval=1))
        ax.xaxis.set_major_formatter(DateFormatter('%b %d'))
        for tick in ax.xaxis.get_major_ticks():
            tick.label1.set_rotation('vertical')
            tick.label1.set_size(11)
        ax.grid(which='major'axis='x'color='#7f7fbf'lw=1.0,ls='-'alpha=1.0zorder=4)
        ax.grid(which='minor'axis='x'color='#e7e7ff'lw=0.8,ls='-'alpha=1.0zorder=2)

    def __chart_year_x_axis (selfaxyearxlab=None):
        xmin = date(year,1,1)
        xmax = (date(year+1,1,1) - timedelta(1))
        xt0 = [date(yearm+11for m in range(12)]+[date(year,12,31)]
        xt1 = [date(year, (n//4)+17*((n%4)+1)) for n in range(012*4) ]
        # X-Axis...
        if xlab: ax.set_xlabel(xlabfontsize=11)
        ax.xaxis_date()
        ax.set_xlim(xminxmax)
        ax.set_xticks(xt0)
        ax.set_xticks(xt1minor=True)
        ax.xaxis.set_major_formatter(DateFormatter('%b %d'))
        for tick in ax.xaxis.get_major_ticks():
            tick.label1.set_rotation('vertical')
            tick.label1.set_size(11)
        ax.grid(which='major'axis='x'color='#3f3f3f'lw=1.3,ls='-'alpha=1.0zorder=4)
        ax.grid(which='minor'axis='x'color='#afafaf'lw=0.8,ls='-'alpha=1.0zorder=2)

    def __chart_decade_x_axis(selfaxyearxlab=None):
        xmin = date(year,1,1)
        xmax = (date(year+10,1,1) - timedelta(1))
        xt0 = [date(year+y11for y in range(10)]
        xt1 = [date(year+(n//12), (n%12)+11for n in range(010*12)]
        # X-Axis...
        if xlab: ax.set_xlabel(xlabfontsize=11)
        ax.xaxis_date()
        ax.set_xlim(xminxmax)
        ax.set_xticks(xt0)
        ax.set_xticks(xt1minor=True)
        ax.xaxis.set_major_formatter(DateFormatter('%Y'))
        for tick in ax.xaxis.get_major_ticks():
            tick.label1.set_rotation('vertical')
            tick.label1.set_size(11)
        ax.grid(which='major'axis='x'color='#3f3f3f'lw=1.3,ls='-'alpha=1.0zorder=4)
        ax.grid(which='minor'axis='x'color='#afafaf'lw=0.8,ls='-'alpha=1.0zorder=2)

    ##__________________________________________________________________________________##
    def __chart_y_axis_temperature (selfaxylab=Noneybot=-40.0ytop=+120.0):
        # Y-Axis...
        if ylab: ax.set_ylabel(ylabfontsize=11)
        ax.set_ylim(ybotytop)
        ax.yaxis.set_major_locator(MultipleLocator(10.0))
        ax.yaxis.set_minor_locator(MultipleLocator5.0))
        ax.grid(which='major'axis='y'color='#7f7f7f'lw=0.9,ls='-'alpha=1.0zorder=3)
        ax.grid(which='minor'axis='y'color='#cfcfcf'lw=0.7,ls='-'alpha=1.0zorder=1)
        ax.axhline(0.0color='black'lw=2.0alpha=1.00zorder=6)

    def __chart_y_axis_precip (selfaxylab=Noneytop=+5.0):
        # Y-Axis...
        if ylab: ax.set_ylabel(ylabfontsize=11)
        ax.set_ylim(0.0ytop)
        ax.yaxis.set_major_locator(MultipleLocator(1.0))
        ax.yaxis.set_minor_locator(MultipleLocator(0.25))
        ax.grid(which='major'axis='y'color='#7f7f7f'lw=0.9,ls='-'alpha=1.0zorder=3)
        ax.grid(which='minor'axis='y'color='#cfcfcf'lw=0.7,ls='-'alpha=1.0zorder=1)

    ##__________________________________________________________________________________##
    def __chart_month_save (selffigfilename):
        fig.set_figwidth(6.4)
        fig.set_figheight(4.8)
        fig.subplots_adjust(left=0.12bottom=0.16right=0.97top=0.92)
        fig.savefig(filename)
        #plt.show()

    def __chart_year_save (selffigfilename):
        fig.set_figwidth(9.8)
        fig.set_figheight(4.8)
        fig.subplots_adjust(left=0.10bottom=0.18right=0.97top=0.92)
        fig.savefig(filename)
        #plt.show()

    def __chart_decade_save (selffigfilename):
        fig.set_figwidth(10.8)
        fig.set_figheight(4.8)
        fig.subplots_adjust(left=0.10bottom=0.15right=0.97top=0.92)
        fig.savefig(filename)
        #plt.show()

    ##__________________________________________________________________________________##
    def chart_temp_min_max (selffname='temp_min_max.png'):
        self.Log.debug('db[%s] Chart(temp.min/max)' % self.name)
        fn = path.join(self.imgsPath,fname)
        sql = Queries['temp.min-max.c']
        rs = self.db_query(sql % self.name)
        if not rs: self.Log.warn('No data!'); return []
        # Munch on the query results...
        dts,series1,series2 = zip(*rs)
        props = {
            'ttl':'Temperature (hi/lo) MSP 2010-2017',
            'xlab':'Days''ylab':'Temperature',
            'xlim':(0max(len(series1),len(series2))),
            'ylim':(min(series2), max(series1)),
            'xLoc':120'xloc':30,
            'yLoc':10.0'yloc':5.0'yFmt':'%+d',
            'Grid':'both''grid':'both''minor':True
        }
        figax = self.__chart_init(props)
        ax.axhline(0.0lw=2.0color='k'zorder=5)
        ax.plot(series1label='Hi'color='#df3f00'alpha=0.75zorder=9)
        ax.plot(series2label='Lo'color='#003fdf'alpha=0.75zorder=8)
        ax.legend(loc='lower left'shadow=True)
        #
        fig.set_figwidth(12.0)
        fig.set_figheight(4.0)
        fig.subplots_adjust(left=0.07bottom=0.13right=0.98top=0.90)
        fig.savefig(fn)
        return rs

    def chart_pressure (selffname='pressure.png'):
        self.Log.debug('db[%s] Chart(pressure)' % self.name)
        fn = path.join(self.imgsPath,fname)
        sql = Queries['pressure.c']
        rs = self.db_query(sql % self.name)
        if not rs: self.Log.warn('No data!'); return []
        # Munch on the query results...
        dts,series = zip(*rs)
        series = [x for x in series if isinstance(x,float)]
        props = {
            'ttl':'Pressure (avg) MSP 2010-2017',
            'xlab':'Days''ylab':'Air Pressure',
            'xlim':(0len(series)),
            #'ylim':(0.0, 100.0),
            #'xLoc':360, 'xloc':120,
            #'yLoc':10.0, 'yloc':2.0,
            #'Grid':'both', 'grid':'both', 'minor':True
        }
        figax = self.__chart_init(props)
        ax.plot(serieslabel='Pressure'color='#003fdf'alpha=1.00zorder=9)
        #
        fig.set_figwidth(12.0)
        fig.set_figheight(4.0)
        fig.subplots_adjust(left=0.05bottom=0.13right=0.98top=0.90)
        fig.savefig(fn)
        return rs

    def chart_rel_hum (selffname='rel_hum.png'):
        self.Log.debug('db[%s] Chart(rel_humid)' % self.name)
        fn = path.join(self.imgsPath,fname)
        sql = Queries['rel_hum.c']
        rs = self.db_query(sql % self.name)
        if not rs: self.Log.warn('No data!'); return []
        # Munch on the query results...
        dts,series = zip(*rs)
        props = {
            'ttl':'Relative Humidity (avg) MSP 2010-2017',
            'xlab':'Days''ylab':'Relative Humidity',
            'xlim':(0len(series)),
            'ylim':(0.0100.0),
            'xLoc':360'xloc':120,
            'yLoc':10.0'yloc':2.0,
            'Grid':'both''grid':'both''minor':True
        }
        figax = self.__chart_init(props)
        ax.plot(serieslabel='Rel.Humidity'color='#003fdf'alpha=1.00zorder=9)
        #
        fig.set_figwidth(12.0)
        fig.set_figheight(4.0)
        fig.subplots_adjust(left=0.05bottom=0.13right=0.98top=0.90)
        fig.savefig(fn)
        return rs

    ##__________________________________________________________________________________##
    def __chart_init (selfprops):
        figax = plt.subplots()
        if 'ttl' in props: fig.suptitle(props['ttl'])
        if 'xlab' in props: ax.set_xlabel(props['xlab'])
        if 'ylab' in props: ax.set_ylabel(props['ylab'])
        if 'xlim' in props: ax.set_xlim(props['xlim'][0],props['xlim'][1])
        if 'ylim' in props: ax.set_ylim(props['ylim'][0],props['ylim'][1])
        if 'xLoc' in props: ax.xaxis.set_major_locator(MultipleLocator(props['xLoc']))
        if 'yLoc' in props: ax.yaxis.set_major_locator(MultipleLocator(props['yLoc']))
        if 'xFmt' in props: ax.xaxis.set_major_formatter(FormatStrFormatter(props['xFmt']))
        if 'yFmt' in props: ax.yaxis.set_major_formatter(FormatStrFormatter(props['yFmt']))
        if 'Grid' in props: ax.grid(which='major'axis=props['Grid'], color='#9f9f9f'lw=1.2,ls='-'alpha=1.0,zorder=1)
        ax.tick_params(which='major'axis='both'direction='out')
        if 'minor' in props:
            ax.minorticks_on()
            if 'xloc' in props: ax.xaxis.set_minor_locator(MultipleLocator(props['xloc']))
            if 'yloc' in props: ax.yaxis.set_minor_locator(MultipleLocator(props['yloc']))
            if 'grid' in props: ax.grid(which='minor'axis=props['grid'], color='#e7e7e7'lw=0.6,ls='-'alpha=1.0,zorder=0)
            ax.tick_params(which='minor'axis='both'direction='out')
        return figax

    ##__________________________________________________________________________________##


##------------------------------------------------------------------------------------------------##
Queries = {
'list':'''
SELECT date, COUNT(station), MIN(hr_drybulb_tempf), MAX(hr_drybulb_tempf),
AVG(hr_rel_hum), AVG(hr_wind_spd), AVG(hr_station_press)
FROM "%s"
WHERE (substr(report_type,1,2)='FM')
GROUP BY date
ORDER BY date,time DESC
'''
,
'report-types':'''
SELECT report_type, COUNT(station)
FROM "%s"
GROUP BY report_type
ORDER BY report_type
'''
## - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
,
'month.temps.c':'''
SELECT
    cast(substr(date,1,4) as INTEGER) AS "dtY",
    cast(substr(date,6,2) as INTEGER) AS "dtM",
    cast(substr(date,9,2) as INTEGER) AS "dtD",
    day_min_drybulb_temp AS "Lo",
    day_max_drybulb_temp AS "Hi"
FROM "%s"
WHERE (report_type=?) AND (substr(date,1,4)=?) AND (substr(date,6,2)=?)
ORDER BY date
'''
,
'month.precip.c':'''
SELECT
    cast(substr(date,1,4) as INTEGER) AS "dtY",
    cast(substr(date,6,2) as INTEGER) AS "dtM",
    cast(substr(date,9,2) as INTEGER) AS "dtD",
    day_precip, day_snowfall
FROM "%s"
WHERE (report_type=?) AND (substr(date,1,4)=?) AND (substr(date,6,2)=?)
ORDER BY date
'''
## - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
,
'year.temps.c':'''
SELECT
    cast(substr(date,1,4) as INTEGER) AS "dtY",
    cast(substr(date,6,2) as INTEGER) AS "dtM",
    cast(substr(date,9,2) as INTEGER) AS "dtD",
    day_min_drybulb_temp AS "Lo",
    day_max_drybulb_temp AS "Hi"
FROM "%s"
WHERE (report_type=?) AND (substr(date,1,4)=?)
ORDER BY date
'''
,
'year.precip.c':'''
SELECT
    cast(substr(date,1,4) as INTEGER) AS "dtY",
    cast(substr(date,6,2) as INTEGER) AS "dtM",
    cast(substr(date,9,2) as INTEGER) AS "dtD",
    day_precip, day_snowfall
FROM "%s"
WHERE (report_type=?) AND (substr(date,1,4)=?)
ORDER BY date
'''
## - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
,
'decade.temps.c':'''
SELECT
    cast(substr(date,1,4) as INTEGER) AS "dtY",
    cast(substr(date,6,2) as INTEGER) AS "dtM",
    cast(substr(date,9,2) as INTEGER) AS "dtD",
    day_min_drybulb_temp AS "Lo",
    day_max_drybulb_temp AS "Hi"
FROM "%s"
WHERE (report_type=?) AND (substr(date,1,4) BETWEEN ? AND ?)
ORDER BY date
'''
,
'decade.precip.c':'''
SELECT
    cast(substr(date,1,4) as INTEGER) AS "dtY",
    cast(substr(date,6,2) as INTEGER) AS "dtM",
    cast(substr(date,9,2) as INTEGER) AS "dtD",
    day_precip, day_snowfall
FROM "%s"
WHERE (report_type=?) AND (substr(date,1,4) BETWEEN ? AND ?)
ORDER BY date
'''
## - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
,
'months.temps.c':'''
SELECT
    cast(substr(date,1,4) as INTEGER) AS "dtY",
    cast(substr(date,6,2) as INTEGER) AS "dtM",
    cast(substr(date,9,2) as INTEGER) AS "dtD",
    day_max_drybulb_temp AS "Hi",
    day_min_drybulb_temp AS "Lo"
FROM "%s"
WHERE (report_type=?)
    AND (substr(date,6,2)=?)
    AND (substr(date,1,4) BETWEEN ? AND ?)
ORDER BY date
'''
,
'months.temps.hi.c':'''
SELECT
    cast(substr(date,1,4) as INTEGER) AS "dtY",
    cast(substr(date,6,2) as INTEGER) AS "dtM",
    cast(substr(date,9,2) as INTEGER) AS "dtD",
    day_max_drybulb_temp AS "Hi"
FROM "%s"
WHERE (report_type=?)
    AND (substr(date,6,2)=?)
    AND (substr(date,1,4) BETWEEN ? AND ?)
ORDER BY date
'''
,
'months.temps.lo.c':'''
SELECT
    cast(substr(date,1,4) as INTEGER) AS "dtY",
    cast(substr(date,6,2) as INTEGER) AS "dtM",
    cast(substr(date,9,2) as INTEGER) AS "dtD",
    day_min_drybulb_temp AS "Lo"
FROM "%s"
WHERE (report_type=?)
    AND (substr(date,6,2)=?)
    AND (substr(date,1,4) BETWEEN ? AND ?)
ORDER BY date
'''
## - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
,
'temp.min-max.c':'''
SELECT date, day_max_drybulb_temp, day_min_drybulb_temp
FROM "%s"
WHERE (report_type='SOD') AND ('2010-01-01' <= date)
ORDER BY date
'''
,
'pressure.c':'''
SELECT date, day_avg_station_press
FROM "%s"
WHERE (report_type='SOD')
ORDER BY date
'''
,
'rel_hum.c':'''
SELECT date, AVG(hr_rel_hum)
FROM "%s"
WHERE (report_type='FM-12')
GROUP BY date
ORDER BY date
'''
## - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
,
'_sql':'''
SELECT * from %s order by date,time
'''
}


####################################################################################################
'''eof'''