Ejemplo n.º 1
0
def _do_month(month, axes, data, in_sts, in_ets, kwargs):
    """Place data on this axes"""
    axes.get_xaxis().set_visible(False)
    axes.get_yaxis().set_visible(False)
    pos = axes.get_position()
    ndcheight = (pos.y1 - pos.y0)
    ndcwidth = (pos.x1 - pos.x0)

    fitbox(plt.gcf(),
           month.strftime("%B %Y"),
           pos.x0,
           pos.x1,
           pos.y1,
           pos.y1 + 0.028,
           ha='center')

    axes.add_patch(
        Rectangle((0., 0.90),
                  1,
                  0.1,
                  zorder=2,
                  facecolor='tan',
                  edgecolor='tan'))

    sts = datetime.date(month.year, month.month, 1)
    ets = (sts + datetime.timedelta(days=35)).replace(day=1)

    calendar.setfirstweekday(calendar.SUNDAY)
    weeks = len(calendar.monthcalendar(month.year, month.month))
    now = sts
    row = 0
    dy = 0.9 / float(weeks)
    dx = 1. / 7.
    for i, dow in enumerate(['SUN', 'MON', 'TUE', 'WED', 'THU', 'FRI', 'SAT']):
        axes.text(1. / 7. * (i + 0.5),
                  0.94,
                  dow,
                  fontsize=fontscale(ndcwidth / 7. * 0.4),
                  ha='center',
                  va='center')
    while now < ets:
        # Is this Sunday?
        if now.weekday() == 6 and now != sts:
            row += 1
        if now < in_sts or now > in_ets:
            now += datetime.timedelta(days=1)
            continue
        offx = (now.weekday() + 1) if now.weekday() != 6 else 0
        axes.text(offx * dx + 0.01,
                  0.9 - row * dy - 0.01,
                  str(now.day),
                  fontsize=fontscale(ndcheight / 5. * 0.25),
                  color='tan',
                  va='top')
        _do_cell(axes, now, data, row, dx, dy, kwargs)
        now += datetime.timedelta(days=1)
Ejemplo n.º 2
0
def calendar_plot(sts, ets, data, **kwargs):
    """Create a plot that looks like a calendar

    Args:
      sts (datetime.date): start date of this plot
      ets (datetime.date): end date of this plot (inclusive)
      data (dict[dict]): dictionary with keys of dates and dicts for
        `val` value and optionally `color` for color
      kwargs (dict):
        heatmap (bool): background color for cells based on `val`, False
        cmap (str): color map to use for norm
    """
    bounds = _compute_bounds(sts, ets)
    # Compute the number of month calendars we need.

    # We want 'square' boxes for each month's calendar, 4x3
    fig = plt.figure(figsize=(10.24, 7.68))
    if 'fontsize' not in kwargs:
        kwargs['fontsize'] = 12
        if len(bounds) < 3:
            kwargs['fontsize'] = 18
        elif len(bounds) < 5:
            kwargs['fontsize'] = 16
        elif len(bounds) < 10:
            kwargs['fontsize'] = 14
    if kwargs.get('heatmap', False):
        kwargs['cmap'] = plt.get_cmap(kwargs.get('cmap', 'viridis'))
        maxval = -1000
        for key in data:
            if data[key]['val'] > maxval:
                maxval = data[key]['val']
        # Need at least 3 slots
        maxval = 5 if maxval < 5 else maxval
        kwargs['norm'] = mpcolors.BoundaryNorm(np.arange(0, maxval),
                                               kwargs['cmap'].N)
    for month in bounds:
        ax = fig.add_axes(bounds[month])
        _do_month(month, ax, data, sts, ets, kwargs)

    iemlogo(fig)
    title = kwargs.get('title')
    if title is not None:
        fitbox(fig, title, 0.1, 0.99, 0.95, 0.99)

    subtitle = kwargs.get('subtitle')
    if subtitle is not None:
        fitbox(fig, subtitle, 0.1, 0.99, 0.925, 0.945)

    return fig
Ejemplo n.º 3
0
def calendar_plot(sts, ets, data, **kwargs):
    """Create a plot that looks like a calendar

    Args:
      sts (datetime.date): start date of this plot
      ets (datetime.date): end date of this plot (inclusive)
      data (dict[dict]): dictionary with keys of dates and dicts for
        `val` value and optionally `color` for color
      kwargs (dict):
        heatmap (bool): background color for cells based on `val`, False
        cmap (str): color map to use for norm
    """
    bounds = _compute_bounds(sts, ets)
    # Compute the number of month calendars we need.

    # We want 'square' boxes for each month's calendar, 4x3
    fig = plt.figure(figsize=(10.24, 7.68))
    if 'fontsize' not in kwargs:
        kwargs['fontsize'] = 12
        if len(bounds) < 3:
            kwargs['fontsize'] = 18
        elif len(bounds) < 5:
            kwargs['fontsize'] = 16
        elif len(bounds) < 10:
            kwargs['fontsize'] = 14
    if kwargs.get('heatmap', False):
        kwargs['cmap'] = plt.get_cmap(kwargs.get('cmap', 'viridis'))
        maxval = -1000
        for key in data:
            if data[key]['val'] > maxval:
                maxval = data[key]['val']
        # Need at least 3 slots
        maxval = 5 if maxval < 5 else maxval
        kwargs['norm'] = mpcolors.BoundaryNorm(np.arange(0, maxval),
                                               kwargs['cmap'].N)
    for month in bounds:
        ax = fig.add_axes(bounds[month])
        _do_month(month, ax, data, sts, ets, kwargs)

    iemlogo(fig)
    title = kwargs.get('title')
    if title is not None:
        fitbox(fig, title, 0.1, 0.99, 0.95, 0.99)

    subtitle = kwargs.get('subtitle')
    if subtitle is not None:
        fitbox(fig, subtitle, 0.1, 0.99, 0.925, 0.945)

    return fig
Ejemplo n.º 4
0
def _do_month(month, axes, data, in_sts, in_ets, kwargs):
    """Place data on this axes"""
    axes.get_xaxis().set_visible(False)
    axes.get_yaxis().set_visible(False)
    pos = axes.get_position()
    ndcheight = (pos.y1 - pos.y0)
    ndcwidth = (pos.x1 - pos.x0)

    fitbox(
        plt.gcf(), month.strftime("%B %Y"),
        pos.x0, pos.x1, pos.y1, pos.y1 + 0.028, ha='center'
    )

    axes.add_patch(Rectangle((0., 0.90), 1, 0.1, zorder=2,
                             facecolor='tan', edgecolor='tan'))

    sts = datetime.date(month.year, month.month, 1)
    ets = (sts + datetime.timedelta(days=35)).replace(day=1)

    calendar.setfirstweekday(calendar.SUNDAY)
    weeks = len(calendar.monthcalendar(month.year, month.month))
    now = sts
    row = 0
    dy = 0.9 / float(weeks)
    dx = 1. / 7.
    for i, dow in enumerate(['SUN', 'MON', 'TUE', 'WED', 'THU',
                             'FRI', 'SAT']):
        axes.text(
            1. / 7. * (i + 0.5), 0.94, dow,
            fontsize=fontscale(ndcwidth / 7. * 0.4),
            ha='center', va='center')
    while now < ets:
        # Is this Sunday?
        if now.weekday() == 6 and now != sts:
            row += 1
        if now < in_sts or now > in_ets:
            now += datetime.timedelta(days=1)
            continue
        offx = (now.weekday() + 1) if now.weekday() != 6 else 0
        axes.text(
            offx * dx + 0.01,
            0.9 - row * dy - 0.01,
            str(now.day), fontsize=fontscale(ndcheight / 5. * 0.25),
            color='tan',
            va='top'
        )
        _do_cell(axes, now, data, row, dx, dy, kwargs)
        now += datetime.timedelta(days=1)
Ejemplo n.º 5
0
def _do_cell(axes, now, data, row, dx, dy, kwargs):
    """Do what work is necessary within the cell"""
    val = data.get(now, dict()).get("val")
    cellcolor = ("None" if kwargs.get("norm") is None or val is None else
                 kwargs["cmap"](kwargs["norm"]([val]))[0])
    offx = (now.weekday() + 1) if now.weekday() != 6 else 0
    cellcolor = data.get(now, dict()).get("cellcolor", cellcolor)
    rect = Rectangle(
        (offx * dx, 0.9 - (row + 1) * dy),
        dx,
        dy,
        zorder=(2 if val is None else 3),
        facecolor=cellcolor,
        edgecolor="tan" if val is None else "k",
    )
    axes.add_patch(rect)
    if val is None:
        return
    color = "k"
    if not isinstance(cellcolor, str):  # this is a string comp here
        color = ("k" if
                 (cellcolor[0] * 256 * 0.299 + cellcolor[1] * 256 * 0.587 +
                  cellcolor[2] * 256 * 0.114) > 186 else "white")
    color = data[now].get("color", color)
    # We need to translate the axes NDC coordinates back to the figure coords
    bbox = axes.get_position()
    sdx = (bbox.x1 - bbox.x0) * dx
    sdy = (bbox.y1 - bbox.y0) * dy
    x0 = bbox.x0 + offx * sdx
    ytop = bbox.y0 + (bbox.y1 - bbox.y0) * 0.9
    y0 = ytop - (row + 1) * sdy
    fitbox(
        plt.gcf(),
        val,
        x0,
        x0 + sdx,
        y0,
        y0 + sdy * 0.55,
        ha="center",
        va="center",
        color=color,
    )
Ejemplo n.º 6
0
def calendar_plot(sts, ets, data, **kwargs):
    """Create a plot that looks like a calendar

    Args:
      sts (datetime.date): start date of this plot
      ets (datetime.date): end date of this plot (inclusive)
      data (dict[dict]): dictionary with keys of dates and dicts for
        `val` value and optionally `color` for color
      kwargs (dict):
        heatmap (bool): background color for cells based on `val`, False
        cmap (str): color map to use for norm
    """
    bounds = _compute_bounds(sts, ets)
    # Compute the number of month calendars we need.

    # We want 'square' boxes for each month's calendar, 4x3
    fig = plt.figure(figsize=(10.24, 7.68))
    if "fontsize" not in kwargs:
        kwargs["fontsize"] = 12
        if len(bounds) < 3:
            kwargs["fontsize"] = 18
        elif len(bounds) < 5:
            kwargs["fontsize"] = 16
        elif len(bounds) < 10:
            kwargs["fontsize"] = 14
    if kwargs.get("heatmap", False):
        kwargs["cmap"] = get_cmap(kwargs.get("cmap", "viridis"))
        maxval = -1000
        for key in data:
            if data[key]["val"] > maxval:
                maxval = data[key]["val"]
        # Need at least 3 slots
        maxval = 5 if maxval < 5 else maxval
        # Need to have more colors than bins
        kwargs["norm"] = mpcolors.BoundaryNorm(
            np.arange(0, maxval,
                      int(maxval / 255.0) + 1), kwargs["cmap"].N)
    for month in bounds:
        ax = fig.add_axes(bounds[month])
        _do_month(month, ax, data, sts, ets, kwargs)

    iemlogo(fig)
    title = kwargs.get("title")
    if title is not None:
        fitbox(fig, title, 0.1, 0.99, 0.95, 0.99)

    subtitle = kwargs.get("subtitle")
    if subtitle is not None:
        if subtitle.find("\n") > 0:  # Allow more room
            fitbox(fig, subtitle, 0.1, 0.99, 0.909, 0.949)
        else:
            fitbox(fig, subtitle, 0.1, 0.99, 0.925, 0.945)

    return fig
Ejemplo n.º 7
0
def plotter(fdict):
    """ Go """
    ctx = get_autoplot_context(fdict, get_description())
    typ = ctx["typ"]
    sort = ctx["sort"]
    date = ctx["date"]

    pgconn = get_dbconn("postgis")
    sts = utc(date.year, date.month, date.day)
    ets = sts + datetime.timedelta(hours=24)

    opts = {
        "W": {
            "fnadd": "-wfo",
            "sortby": "wfo ASC, phenomena ASC, eventid ASC",
        },
        "S": {
            "fnadd": "",
            "sortby": "size DESC"
        },
        "T": {
            "fnadd": "-time",
            "sortby": "issue ASC"
        },
    }
    phenoms = {"W": ["TO", "SV"], "F": ["FF"], "M": ["MA"]}

    # Defaults
    thumbpx = 100
    cols = 10
    mybuffer = 10000
    header = 35

    # Find largest polygon either in height or width
    gdf = read_postgis(
        """
        SELECT wfo, phenomena, eventid, issue,
        ST_area2d(ST_transform(geom,2163)) as size,
        (ST_xmax(ST_transform(geom,2163)) +
         ST_xmin(ST_transform(geom,2163))) /2.0 as xc,
        (ST_ymax(ST_transform(geom,2163)) +
         ST_ymin(ST_transform(geom,2163))) /2.0 as yc,
        ST_transform(geom, 2163) as utmgeom,
        (ST_xmax(ST_transform(geom,2163)) -
         ST_xmin(ST_transform(geom,2163))) as width,
        (ST_ymax(ST_transform(geom,2163)) -
         ST_ymin(ST_transform(geom,2163))) as height
        from sbw_""" + str(sts.year) + """
        WHERE status = 'NEW' and issue >= %s and issue < %s and
        phenomena IN %s and eventid is not null
        ORDER by """ + opts[sort]["sortby"] + """
    """,
        pgconn,
        params=(sts, ets, tuple(phenoms[typ])),
        geom_col="utmgeom",
        index_col=None,
    )

    # For size reduction work
    df = read_sql(
        """
        SELECT w.wfo, phenomena, eventid,
        sum(ST_area2d(ST_transform(u.geom,2163))) as county_size
        from
        warnings_""" + str(sts.year) + """ w JOIN ugcs u on (u.gid = w.gid)
        WHERE issue >= %s and issue < %s and
        significance = 'W' and phenomena IN %s
        GROUP by w.wfo, phenomena, eventid
    """,
        pgconn,
        params=(sts, ets, tuple(phenoms[typ])),
        index_col=["wfo", "phenomena", "eventid"],
    )
    # Join the columns
    gdf = gdf.merge(df, on=["wfo", "phenomena", "eventid"])
    gdf["ratio"] = (1.0 - (gdf["size"] / gdf["county_size"])) * 100.0

    # Make mosaic image
    events = len(df.index)
    rows = int(events / cols) + 1
    if events % cols == 0:
        rows -= 1
    if rows == 0:
        rows = 1
    ypixels = (rows * thumbpx) + header
    fig = plt.figure(figsize=(thumbpx * cols / 100.0, ypixels / 100.0))
    plt.axes([0, 0, 1, 1], facecolor="black")

    imagemap = StringIO()
    utcnow = utc()
    imagemap.write("<!-- %s %s -->\n" %
                   (utcnow.strftime("%Y-%m-%d %H:%M:%S"), sort))
    imagemap.write("<map name='mymap'>\n")

    # Write metadata to image
    mydir = os.sep.join(
        [os.path.dirname(os.path.abspath(__file__)), "../../../images"])
    logo = mpimage.imread("%s/logo_reallysmall.png" % (mydir, ))
    y0 = fig.get_figheight() * 100.0 - logo.shape[0] - 5
    fig.figimage(logo, 5, y0, zorder=3)

    i = 0
    # amount of NDC y space we have for axes plotting
    ytop = 1 - header / float((rows * 100) + header)
    dy = ytop / float(rows)
    ybottom = ytop

    # Sumarize totals
    y = ytop
    dy2 = (1.0 - ytop) / 2.0
    for phenomena, df2 in gdf.groupby("phenomena"):
        car = (1.0 - df2["size"].sum() / df2["county_size"].sum()) * 100.0
        fitbox(
            fig,
            ("%i %s.W: Avg size %5.0f km^2 CAR: %.0f%%") %
            (len(df2.index), phenomena, df2["size"].mean() / 1e6, car),
            0.8,
            0.99,
            y,
            y + dy2,
            color=COLORS[phenomena],
        )
        y += dy2

    fitbox(
        fig,
        "NWS %s Storm Based Warnings issued %s UTC" % (
            " + ".join([VTEC_PHENOMENA[p] for p in phenoms[typ]]),
            sts.strftime("%d %b %Y"),
        ),
        0.05,
        0.79,
        ytop + dy2,
        0.999,
        color="white",
    )
    fitbox(
        fig,
        "Generated: %s UTC, IEM Autplot #203" %
        (utcnow.strftime("%d %b %Y %H:%M:%S"), ),
        0.05,
        0.79,
        ytop,
        0.999 - dy2,
        color="white",
    )
    # We want to reserve 14pts at the bottom and buffer the plot by 10km
    # so we compute this in the y direction, since it limits us
    max_dimension = max([gdf["width"].max(), gdf["height"].max()])
    yspacing = max_dimension / 2.0 + mybuffer
    xspacing = yspacing * 1.08  # approx

    for _, row in gdf.iterrows():
        # - Map each polygon
        x0 = float(row["xc"]) - xspacing
        x1 = float(row["xc"]) + xspacing
        y0 = float(row["yc"]) - yspacing - (yspacing * 0.14)
        y1 = float(row["yc"]) + yspacing - (yspacing * 0.14)

        col = i % 10
        if col == 0:
            ybottom -= dy
        ax = plt.axes(
            [col * 0.1, ybottom, 0.1, dy],
            facecolor="black",
            xticks=[],
            yticks=[],
            aspect="auto",
        )
        for x in ax.spines:
            ax.spines[x].set_visible(False)
        ax.set_xlim(x0, x1)
        ax.set_ylim(y0, y1)
        for poly in row["utmgeom"]:
            xs, ys = poly.exterior.xy
            color = COLORS[row["phenomena"]]
            ax.plot(xs, ys, color=color, lw=2)

        car = "NA"
        carColor = "white"
        if not pd.isnull(row["ratio"]):
            carf = row["ratio"]
            car = "%.0f" % (carf, )
            if carf > 75:
                carColor = "green"
            if carf < 25:
                carColor = "red"

        # Draw Text!
        issue = row["issue"]
        s = "%s.%s.%s.%s" % (
            row["wfo"],
            row["phenomena"],
            row["eventid"],
            issue.strftime("%H%M"),
        )
        # (w, h) = font10.getsize(s)
        # print s, h
        ax.text(
            0,
            0,
            s,
            transform=ax.transAxes,
            color="white",
            va="bottom",
            fontsize=7,
        )
        s = "%.0f sq km %s%%" % (row["size"] / 1000000.0, car)
        ax.text(
            0,
            0.1,
            s,
            transform=ax.transAxes,
            color=carColor,
            va="bottom",
            fontsize=7,
        )

        # Image map
        url = ("/vtec/#%s-O-NEW-K%s-%s-%s-%04i") % (
            sts.year,
            row["wfo"],
            row["phenomena"],
            "W",
            row["eventid"],
        )
        altxt = "Click for text/image"
        pos = ax.get_position()
        mx0 = pos.x0 * 1000.0
        my = (1.0 - pos.y1) * ypixels
        imagemap.write(
            ('<area href="%s" alt="%s" title="%s" '
             'shape="rect" coords="%.0f,%.0f,%.0f,%.0f">\n') %
            (url, altxt, altxt, mx0, my, mx0 + thumbpx, my + thumbpx))
        i += 1

    faux = plt.axes([0, 0, 1, 1], facecolor="None", zorder=100)
    for i in range(1, rows):
        faux.axhline(i * dy, lw=1.0, color="blue")

    imagemap.write("</map>")
    imagemap.seek(0)

    if gdf.empty:
        fitbox(fig, "No warnings Found!", 0.2, 0.8, 0.2, 0.5, color="white")

    df = gdf.drop("utmgeom", axis=1)
    return fig, df, imagemap.read()
Ejemplo n.º 8
0
def plotter(fdict):
    """ Go """
    pgconn = get_dbconn('asos')

    ctx = get_autoplot_context(fdict, get_description())
    station = ctx['zstation']
    h1 = int(ctx['h1'])
    h2 = int(ctx['h2'])
    varname = ctx['v']

    tzname = ctx['_nt'].sts[station]['tzname']

    df = read_sql("""
    WITH data as (
        SELECT valid at time zone %s + '10 minutes'::interval as localvalid,
        date_trunc(
             'hour', valid at time zone %s  + '10 minutes'::interval) as v,
        tmpf, dwpf, sknt, drct, alti, relh, random() as r,
        coalesce(mslp, alti * 33.8639, 1013.25) as slp
        from alldata where station = %s and report_type = 2
        and extract(hour from valid at time zone %s + '10 minutes'::interval)
        in (%s, %s)),
     agg as (
          select *, extract(hour from v) as hour,
          rank() OVER (PARTITION by v ORDER by localvalid ASC, r ASC) from data
     )

     SELECT *, date(
         case when hour = %s
         then date(v - '1 day'::interval)
         else date(v) end) from agg WHERE rank = 1
    """,
                  pgconn,
                  params=(tzname, tzname, station, tzname, h1, h2,
                          h2 if h2 < h1 else -1),
                  index_col=None)
    if df.empty:
        raise NoDataFound("No data was found.")
    if varname == 'q':
        df['pressure'] = mcalc.add_height_to_pressure(
            df['slp'].values * units('millibars'),
            ctx['_nt'].sts[station]['elevation'] * units('m')).to(
                units('millibar'))
        # compute mixing ratio
        df['q'] = mcalc.mixing_ratio_from_relative_humidity(
            df['relh'].values * units('percent'), df['tmpf'].values *
            units('degF'), df['pressure'].values * units('millibars')) * 1000.

    # pivot
    df = df.pivot(index='date', columns='hour', values=varname).reset_index()
    df = df.dropna()
    df['doy'] = pd.to_numeric(pd.to_datetime(df['date']).dt.strftime("%j"))
    df['year'] = pd.to_datetime(df['date']).dt.year
    df['week'] = (df['doy'] / 7).astype(int)
    df['delta'] = df[h2] - df[h1]

    (fig, ax) = plt.subplots(1, 1)
    if ctx['opt'] == 'no':
        ax.set_xlabel("Plotted lines are smoothed over %.0f days" %
                      (ctx['smooth'], ))
    ax.set_ylabel(
        "%s %s Difference" %
        (PDICT[varname], "Accumulated Sum" if ctx['opt'] == 'yes' else ''))

    if ctx['opt'] == 'no':
        # Histogram
        H, xedges, yedges = np.histogram2d(df['doy'].values,
                                           df['delta'].values,
                                           bins=(50, 50))
        ax.pcolormesh(xedges,
                      yedges,
                      H.transpose(),
                      cmap=plt.get_cmap(ctx['cmap']),
                      alpha=0.5)

    # Plot an average line
    gdf = df.groupby('doy').mean().rolling(ctx['smooth'],
                                           min_periods=1,
                                           center=True).mean()
    y = gdf['delta'] if ctx['opt'] == 'no' else gdf['delta'].cumsum()
    ax.plot(gdf.index.values,
            y,
            label='Average',
            zorder=6,
            lw=2,
            color='k',
            linestyle='-.')

    # Plot selected year
    for i in range(1, 5):
        year = ctx.get("y%s" % (i, ))
        if year is None:
            continue
        df2 = df[df['year'] == year]
        if not df2.empty:
            gdf = df2.groupby('doy').mean().rolling(ctx['smooth'],
                                                    min_periods=1,
                                                    center=True).mean()
            y = gdf['delta'] if ctx['opt'] == 'no' else gdf['delta'].cumsum()
            ax.plot(gdf.index.values, y, label=str(year), lw=2, zorder=10)

    ax.set_xticks((1, 32, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 365))
    ax.set_xticklabels(calendar.month_abbr[1:])
    ax.set_xlim(1, 366)
    ax.grid(True)
    ax.legend(loc='best', ncol=5)
    sts = datetime.datetime(2000, 6, 1, h1)
    ets = datetime.datetime(2000, 6, 1, h2)
    title = ("%s [%s] %s Difference (%.0f-%.0f)\n"
             "%s minus %s (%s) (timezone: %s)") % (
                 ctx['_nt'].sts[station]['name'], station, PDICT[varname],
                 df['year'].min(), df['year'].max(), ets.strftime("%-I %p"),
                 sts.strftime("%-I %p"),
                 "same day" if h2 > h1 else "previous day", tzname)
    fitbox(fig, title, 0.05, 0.95, 0.91, 0.99, ha='center')

    return fig, df
Ejemplo n.º 9
0
def plotter(fdict):
    """ Go """
    pgconn = get_dbconn('postgis')
    ctx = get_autoplot_context(fdict, get_description())

    wfo = ctx['station']
    phenomena = ctx['phenomena']
    significance = ctx['significance']
    if ctx['season'] == 'all':
        months = range(1, 13)
    elif ctx['season'] == 'water_year':
        months = range(1, 13)
    elif ctx['season'] == 'spring':
        months = [3, 4, 5]
    elif ctx['season'] == 'spring2':
        months = [4, 5, 6]
    elif ctx['season'] == 'fall':
        months = [9, 10, 11]
    elif ctx['season'] == 'summer':
        months = [6, 7, 8]
    elif ctx['season'] == 'winter':
        months = [12, 1, 2]
    else:
        ts = datetime.datetime.strptime("2000-" + ctx['season'] + "-01",
                                        '%Y-%b-%d')
        # make sure it is length two for the trick below in SQL
        months = [ts.month, 999]

    nt = NetworkTable("WFO")

    (fig, ax) = plt.subplots(1, 1)

    tzname = nt.sts[wfo]['tzname']
    df = read_sql("""
    WITH data as (
        SELECT extract(year from issue) as yr, eventid,
        min(issue at time zone %s) as minissue,
        max(expire at time zone %s) as maxexpire from warnings WHERE
        phenomena = %s and significance = %s
        and wfo = %s and
        extract(month from issue) in %s GROUP by yr, eventid),
    events as (
        select count(*) from data),
    timedomain as (
        SELECT generate_series(minissue,
            least(maxexpire, minissue + '24 hours'::interval)
            , '1 minute'::interval)
        as ts from data
    ),
    data2 as (
        SELECT extract(hour from ts)::int * 60 + extract(minute from ts)::int
        as minute, count(*) from timedomain
        GROUP by minute ORDER by minute ASC)
    select d.minute, d.count, e.count as total from data2 d, events e
    """, pgconn, params=(
        tzname, tzname, phenomena, significance, wfo, tuple(months)),
                  index_col='minute')
    if df.empty:
        raise ValueError("No Results Found")
    df['frequency'] = df['count'] / df['total'] * 100.
    ax.bar(df.index.values, df['frequency'].values, ec='b', fc='b',
           align='center')
    ax.grid()
    if df['frequency'].max() > 70:
        ax.set_ylim(0, 101)
    ax.set_xticks(range(0, 25 * 60, 60))
    ax.set_xlim(-0.5, 24 * 60 + 1)
    ax.set_xticklabels(["Mid", "", "", "3 AM", "", "", "6 AM", "", "", '9 AM',
                        "", "", "Noon", "", "", "3 PM", "", "", "6 PM",
                        "", "", "9 PM", "", "", "Mid"])
    ax.set_xlabel("Timezone: %s (Daylight or Standard)" % (tzname,))
    ax.set_ylabel("Percentage [%%] out of %.0f Events" % (df['total'].max(), ))
    title = "[%s] %s :: Time of Day Frequency" % (wfo, nt.sts[wfo]['name'])
    subtitle = "%s (%s.%s) [%s]" % (
        vtec.get_ps_string(phenomena, significance),
        phenomena, significance, MDICT[ctx['season']]
    )
    fitbox(fig, title, 0.05, 0.95, 0.95, 0.99, ha='center')
    fitbox(fig, subtitle, 0.05, 0.95, 0.91, 0.945, ha='center')

    return fig, df
Ejemplo n.º 10
0
def plotter(fdict):
    """ Go """
    pgconn = get_dbconn("asos")

    ctx = get_autoplot_context(fdict, get_description())
    station = ctx["zstation"]
    date = ctx["date"]
    opt = ctx["opt"]
    varname = ctx["v"]

    tzname = ctx["_nt"].sts[station]["tzname"]

    # Resolve how to limit the query data
    limiter = ""
    if opt == "day":
        limiter = (f" and to_char(valid at time zone '{tzname}', 'mmdd') = "
                   f"'{date.strftime('%m%d')}' ")
        subtitle = (f"For Date of {date.strftime('%-d %b')}, "
                    f"{date.strftime('%-d %b %Y')} plotted in bottom panel")
        datefmt = "%I %p"
    elif opt == "week":
        limiter = f" and extract(week from valid) = {date.strftime('%V')} "
        subtitle = (
            f"For ISO Week of {date.strftime('%V')}, "
            f"week of {date.strftime('%-d %b %Y')} plotted in bottom panel")
        datefmt = "%-d %b"
    elif opt == "month":
        limiter = f" and extract(month from valid) = {date.strftime('%m')} "
        subtitle = (f"For Month of {date.strftime('%B')}, "
                    f"{date.strftime('%b %Y')} plotted in bottom panel")
        datefmt = "%-d"
    else:
        subtitle = f"All Year, {date.year} plotted in bottom panel"
        datefmt = "%-d %b"

    # Load up all the values, since we need pandas to do some heavy lifting
    obsdf = read_sql(
        f"""
        select valid at time zone 'UTC' as utc_valid,
        extract(year from valid at time zone %s)  as year,
        extract(hour from valid at time zone %s +
            '10 minutes'::interval)::int as hr, {varname}
        from alldata WHERE station = %s and {varname} is not null {limiter}
        and report_type = 2 ORDER by valid ASC
    """,
        pgconn,
        params=(tzname, tzname, station),
        index_col=None,
    )
    if obsdf.empty:
        raise NoDataFound("No data was found.")

    # Assign percentiles
    obsdf["quantile"] = obsdf[["hr", varname]].groupby("hr").rank(pct=True)
    # Compute actual percentiles
    qtile = (obsdf[["hr", varname
                    ]].groupby("hr").quantile(np.arange(0, 1.01,
                                                        0.05)).reset_index())
    qtile = qtile.rename(columns={"level_1": "quantile"})
    (fig, ax) = plt.subplots(2, 1)
    cmap = get_cmap(ctx["cmap"])
    for hr, gdf in qtile.groupby("hr"):
        ax[0].plot(
            gdf["quantile"].values * 100.0,
            gdf[varname].values,
            color=cmap(hr / 23.0),
            label=str(hr),
        )
    ax[0].set_xlim(0, 100)
    ax[0].grid(True)
    ax[0].set_ylabel(PDICT[varname])
    ax[0].set_xlabel("Percentile")
    ax[0].set_position([0.13, 0.55, 0.71, 0.34])
    cax = plt.axes([0.86, 0.55, 0.03, 0.33],
                   frameon=False,
                   yticks=[],
                   xticks=[])
    cb = ColorbarBase(cax, cmap=cmap)
    cb.set_ticks(np.arange(0, 1, 4.0 / 24.0))
    cb.set_ticklabels(["Mid", "4 AM", "8 AM", "Noon", "4 PM", "8 PM"])
    cb.set_label("Local Hour")

    thisyear = obsdf[obsdf["year"] == date.year]
    if not thisyear.empty:
        ax[1].plot(thisyear["utc_valid"].values,
                   thisyear["quantile"].values * 100.0)
        ax[1].grid(True)
        ax[1].set_ylabel("Percentile")
        ax[1].set_ylim(-1, 101)
        ax[1].xaxis.set_major_formatter(
            mdates.DateFormatter(datefmt, tz=pytz.timezone(tzname)))
        if opt == "day":
            ax[1].set_xlabel(f"Timezone: {tzname}")
    title = ("%s %s %s Percentiles\n%s") % (
        station,
        ctx["_nt"].sts[station]["name"],
        PDICT[varname],
        subtitle,
    )
    fitbox(fig, title, 0.01, 0.99, 0.91, 0.99, ha="center", va="center")
    return fig, qtile
Ejemplo n.º 11
0
def plotter(fdict):
    """ Go """
    pgconn = get_dbconn("asos")
    ctx = get_autoplot_context(fdict, get_description())

    station = ctx["zstation"]
    month = ctx["month"]
    varname = ctx["var"]
    tzname = ctx["_nt"].sts[station]["tzname"]

    if ctx.get("sdate") and ctx.get("edate"):
        date_limiter = (
            " and (to_char(valid at time zone '%s', 'mmdd') >= '%s'"
            " %s to_char(valid at time zone '%s', 'mmdd') <= '%s')") % (
                tzname,
                ctx["sdate"].strftime("%m%d"),
                "or" if ctx["sdate"] > ctx["edate"] else "and",
                tzname,
                ctx["edate"].strftime("%m%d"),
            )
        title = "between %s and %s" % (
            ctx["sdate"].strftime("%-d %b"),
            ctx["edate"].strftime("%-d %b"),
        )
        if ctx["sdate"] == ctx["edate"]:
            date_limiter = (
                "and to_char(valid at time zone '%s', 'mmdd') = '%s'") % (
                    tzname, ctx["sdate"].strftime("%m%d"))
            title = "on %s" % (ctx["sdate"].strftime("%-d %b"), )
    else:
        if month == "all":
            months = range(1, 13)
        elif month == "fall":
            months = [9, 10, 11]
        elif month == "winter":
            months = [12, 1, 2]
        elif month == "spring":
            months = [3, 4, 5]
        elif month == "summer":
            months = [6, 7, 8]
        elif month == "octmar":
            months = [10, 11, 12, 1, 2, 3]
        else:
            ts = datetime.datetime.strptime("2000-" + month + "-01",
                                            "%Y-%b-%d")
            # make sure it is length two for the trick below in SQL
            months = [ts.month, 999]
        date_limiter = (
            " and extract(month from valid at time zone '%s') in %s") % (
                tzname, tuple(months))
        title = MDICT[month]
    if ctx.get("hour") is not None:
        date_limiter += (
            f" and extract(hour from valid at time zone '{tzname}' "
            f"+ '10 minutes'::interval) = {ctx['hour']}")
        dt = datetime.datetime(2000, 1, 1, ctx["hour"])
        title += " @" + dt.strftime("%-I %p")
    (agg, dbvar) = varname.split("_")
    if agg in ["max", "min"]:
        titlelabel = "Top"
        sorder = "DESC" if agg == "max" else "ASC"
        df = read_sql(
            f"""
            WITH data as (
                SELECT valid at time zone %s as v, {dbvar} from alldata
                WHERE station = %s {date_limiter})

            SELECT v as valid, {dbvar} from data
            ORDER by {dbvar} {sorder} NULLS LAST LIMIT 100
        """,
            pgconn,
            params=(ctx["_nt"].sts[station]["tzname"], station),
            index_col=None,
        )
    else:
        titlelabel = "Most Recent"
        op = ">=" if agg == "above" else "<"
        threshold = float(ctx.get("threshold", 100))
        df = read_sql(
            f"SELECT valid at time zone %s as valid, {dbvar} from alldata "
            f"WHERE station = %s {date_limiter} and {dbvar} {op} {threshold} "
            "ORDER by valid DESC LIMIT 100",
            pgconn,
            params=(ctx["_nt"].sts[station]["tzname"], station),
            index_col=None,
        )
    if df.empty:
        raise NoDataFound("Error, no results returned!")
    ylabels = []
    fmt = "%.0f" if dbvar in ["tmpf", "dwpf"] else "%.2f"
    hours = []
    y = []
    lastval = -99
    ranks = []
    currentrank = 0
    rows2keep = []
    for idx, row in df.iterrows():
        key = row["valid"].strftime("%Y%m%d%H")
        if key in hours or pd.isnull(row[dbvar]):
            continue
        rows2keep.append(idx)
        hours.append(key)
        y.append(row[dbvar])
        lbl = fmt % (row[dbvar], )
        lbl += " -- %s" % (row["valid"].strftime("%b %d, %Y %-I:%M %p"), )
        ylabels.append(lbl)
        if row[dbvar] != lastval or agg in ["above", "below"]:
            currentrank += 1
        ranks.append(currentrank)
        lastval = row[dbvar]
        if len(ylabels) == 10:
            break
    if not y:
        raise NoDataFound("No data found.")

    fig = plt.figure(figsize=(8, 6))
    ax = plt.axes([0.1, 0.1, 0.5, 0.8])
    ax.barh(
        range(len(y), 0, -1),
        y,
        ec="green",
        fc="green",
        height=0.8,
        align="center",
    )
    ax2 = ax.twinx()
    ax2.set_ylim(0.5, 10.5)
    ax.set_ylim(0.5, 10.5)
    ax2.set_yticks(range(1, len(y) + 1))
    ax.set_yticks(range(1, len(y) + 1))
    ax.set_yticklabels(["#%s" % (x, ) for x in ranks][::-1])
    ax2.set_yticklabels(ylabels[::-1])
    ax.grid(True, zorder=11)
    ax.set_xlabel("%s %s" % (METRICS[varname], UNITS[dbvar]))
    ab = ctx["_nt"].sts[station]["archive_begin"]
    if ab is None:
        raise NoDataFound("Unknown station metadata.")
    fitbox(
        fig,
        ("%s [%s] %s 10 Events\n%s %s (%s) (%s-%s)") % (
            ctx["_nt"].sts[station]["name"],
            station,
            titlelabel,
            METRICS[varname],
            ctx.get("threshold") if agg in ["above", "below"] else "",
            title,
            ab.year,
            datetime.datetime.now().year,
        ),
        0.01,
        0.99,
        0.91,
        0.99,
        ha="center",
    )
    fig.text(
        0.98,
        0.03,
        "Timezone: %s" % (ctx["_nt"].sts[station]["tzname"], ),
        ha="right",
    )

    return fig, df.loc[rows2keep]
Ejemplo n.º 12
0
def plotter(fdict):
    """ Go """
    pgconn = get_dbconn("asos")

    ctx = get_autoplot_context(fdict, get_description())
    station = ctx["zstation"]
    h1 = int(ctx["h1"])
    h2 = int(ctx["h2"])
    varname = ctx["v"]

    tzname = ctx["_nt"].sts[station]["tzname"]

    df = read_sql(
        """
    WITH data as (
        SELECT valid at time zone %s + '10 minutes'::interval as localvalid,
        date_trunc(
             'hour', valid at time zone %s  + '10 minutes'::interval) as v,
        tmpf, dwpf, sknt, drct, alti, relh, random() as r,
        coalesce(mslp, alti * 33.8639, 1013.25) as slp
        from alldata where station = %s and report_type = 2
        and extract(hour from valid at time zone %s + '10 minutes'::interval)
        in (%s, %s)),
     agg as (
          select *, extract(hour from v) as hour,
          rank() OVER (PARTITION by v ORDER by localvalid ASC, r ASC) from data
     )

     SELECT *, date(
         case when hour = %s
         then date(v - '1 day'::interval)
         else date(v) end) from agg WHERE rank = 1
    """,
        pgconn,
        params=(
            tzname,
            tzname,
            station,
            tzname,
            h1,
            h2,
            h2 if h2 < h1 else -1,
        ),
        index_col=None,
    )
    if df.empty:
        raise NoDataFound("No data was found.")
    if varname == "q":
        df["pressure"] = mcalc.add_height_to_pressure(
            df["slp"].values * units("millibars"),
            ctx["_nt"].sts[station]["elevation"] * units("m"),
        ).to(units("millibar"))
        # compute mixing ratio
        df["q"] = (mcalc.mixing_ratio_from_relative_humidity(
            df["relh"].values * units("percent"),
            df["tmpf"].values * units("degF"),
            df["pressure"].values * units("millibars"),
        ) * 1000.0)

    # pivot
    df = df.pivot(index="date", columns="hour", values=varname).reset_index()
    df = df.dropna()
    df["doy"] = pd.to_numeric(pd.to_datetime(df["date"]).dt.strftime("%j"))
    df["year"] = pd.to_datetime(df["date"]).dt.year
    df["week"] = (df["doy"] / 7).astype(int)
    df["delta"] = df[h2] - df[h1]

    (fig, ax) = plt.subplots(1, 1)
    if ctx["opt"] == "no":
        ax.set_xlabel("Plotted lines are smoothed over %.0f days" %
                      (ctx["smooth"], ))
    ax.set_ylabel(
        "%s %s Difference" %
        (PDICT[varname], "Accumulated Sum" if ctx["opt"] == "yes" else ""))

    if ctx["opt"] == "no":
        # Histogram
        H, xedges, yedges = np.histogram2d(df["doy"].values,
                                           df["delta"].values,
                                           bins=(50, 50))
        ax.pcolormesh(
            xedges,
            yedges,
            H.transpose(),
            cmap=get_cmap(ctx["cmap"]),
            alpha=0.5,
        )

    # Plot an average line
    gdf = (df.groupby("doy").mean().rolling(ctx["smooth"],
                                            min_periods=1,
                                            center=True).mean())
    y = gdf["delta"] if ctx["opt"] == "no" else gdf["delta"].cumsum()
    ax.plot(
        gdf.index.values,
        y,
        label="Average",
        zorder=6,
        lw=2,
        color="k",
        linestyle="-.",
    )

    # Plot selected year
    for i in range(1, 5):
        year = ctx.get("y%s" % (i, ))
        if year is None:
            continue
        df2 = df[df["year"] == year]
        if not df2.empty:
            gdf = (df2.groupby("doy").mean().rolling(ctx["smooth"],
                                                     min_periods=1,
                                                     center=True).mean())
            y = gdf["delta"] if ctx["opt"] == "no" else gdf["delta"].cumsum()
            ax.plot(gdf.index.values, y, label=str(year), lw=2, zorder=10)

    ax.set_xticks((1, 32, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335))
    ax.set_xticklabels(calendar.month_abbr[1:])
    ax.set_xlim(1, 366)
    ax.grid(True)
    ax.legend(loc="best", ncol=5)
    sts = datetime.datetime(2000, 6, 1, h1)
    ets = datetime.datetime(2000, 6, 1, h2)
    title = ("%s [%s] %s Difference (%.0f-%.0f)\n"
             "%s minus %s (%s) (timezone: %s)") % (
                 ctx["_nt"].sts[station]["name"],
                 station,
                 PDICT[varname],
                 df["year"].min(),
                 df["year"].max(),
                 ets.strftime("%-I %p"),
                 sts.strftime("%-I %p"),
                 "same day" if h2 > h1 else "previous day",
                 tzname,
             )
    fitbox(fig, title, 0.05, 0.95, 0.91, 0.99, ha="center")

    return fig, df
Ejemplo n.º 13
0
def _make_plot(
    station,
    df,
    units,
    nsector,
    rmax,
    hours,
    months,
    sname,
    level,
    bins,
    tzname,
    **kwargs,
):
    """Generate a matplotlib windrose plot

    Args:
      station (str): station identifier
      df (pd.DataFrame): observations
      drct (list): list of wind directions
      units (str): units of wind speed
      nsector (int): number of bins to use for windrose
      rmax (float): radius of the plot
      hours (list): hour limit for plot
      month (list): month limit for plot
      sname (str): station name
      level (int): RAOB level in hPa of interest
      bins (list): values for binning the wind speeds
      tzname (str): Time zone this plot is produced in.

    Returns:
      matplotlib.Figure
    """
    wu = WINDUNITS[units]
    # Filters the missing values
    df2 = df[df["drct"] >= 0]
    direction = df2["drct"].values * mpunits("degree")
    if "speed" in df2.columns:
        speed = df2["speed"].values * wu["units"]
    else:
        speed = df2["sknt"].values * mpunits("knots")
    if not hasattr(bins, "units"):
        bins = wu["bins"] * wu["units"]
        if level is not None:
            bins = RAOB_BINS[units] * wu["units"]
    if len(df2.index) < 5:
        wp = WindrosePlot()
        wp.ax.text(
            0.5,
            0.5,
            "Not Enough Data For Plot.",
            ha="center",
            transform=wp.ax.transAxes,
        )
        return wp.fig
    wp = plot(direction, speed, bins=bins, nsector=nsector, rmax=rmax)

    # Now we put some fancy debugging info on the plot
    tlimit = "[Time Domain: "
    if len(hours) == 24 and len(months) == 12:
        tlimit = ""
    if len(hours) < 24:
        if len(hours) > 4:
            tlimit += "%s-%s" % (
                datetime(2000, 1, 1, hours[0]).strftime("%-I %p"),
                datetime(2000, 1, 1, hours[-1]).strftime("%-I %p"),
            )
        else:
            for h in hours:
                tlimit += "%s," % (datetime(2000, 1, 1, h).strftime("%-I %p"),)
    if len(months) < 12:
        for h in months:
            tlimit += "%s," % (datetime(2000, h, 1).strftime("%b"),)
    if tlimit != "":
        tlimit += "]"
    label = ("[%s] %s%s\n" "Windrose Plot %s\n" "Time Bounds: %s") % (
        station,
        sname if sname is not None else "((%s))" % (station,),
        "" if level is None else " @%s hPa" % (level,),
        tlimit,
        _time_domain_string(df, tzname),
    )
    fitbox(wp.fig, label, 0.14, 0.99, 0.92, 0.99, ha="left")
    label = ("Summary\nobs count: %s\nMissing: %s\nAvg Speed: %.1f %s") % (
        len(df.index),
        len(df.index) - len(df2.index),
        speed.m.mean(),
        units,
    )
    wp.fig.text(0.96, 0.11, label, ha="right", fontsize=14)
    if not kwargs.get("nogenerated", False):
        wp.fig.text(
            0.02,
            0.1,
            "Generated: %s" % (datetime.now().strftime("%d %b %Y"),),
            verticalalignment="bottom",
            fontsize=14,
        )
    # Denote the direction blowing from
    lbl = ("Calm values are < %.1f %s\nArrows indicate wind direction.") % (
        bins.m[0],
        units,
    )
    wp.fig.text(0.02, 0.125, lbl, va="bottom")

    return wp.fig