Пример #1
0
def mcalc_feelslike(tmpf, dwpf, smps):
    """Compute a feels like temperature

    Args:
      temperature (temperature): The dry bulb temperature
      dewpoint (temperature): The dew point temperature
      speed (speed): the wind speed

    Returns:
      temperature (temperature): The feels like temperature
    """
    is_not_scalar = isinstance(tmpf.m, (list, tuple, np.ndarray))
    rh = mcalc.relative_humidity_from_dewpoint(tmpf, dwpf)
    # NB: update this once metpy 0.11 is released
    app = mcalc.apparent_temperature(tmpf, rh, smps)
    if hasattr(app, "mask"):
        if is_not_scalar:
            app[app.mask] = tmpf[app.mask]
        else:
            app = tmpf
    elif hasattr(tmpf, "mask"):
        app = masked_array(app.m, app.units)
        app.mask = tmpf.mask

    return app
Пример #2
0
def test_apparent_temperature_windchill():
    """Test that apparent temperature works when a windchill is calculated."""
    temperature = -5. * units.degC
    rel_humidity = 50. * units.percent
    wind = 35. * units('m/s')
    truth = -18.9357 * units.degC
    res = apparent_temperature(temperature, rel_humidity, wind)
    assert_almost_equal(res, truth, 0)
Пример #3
0
def test_apparent_temperature():
    """Test the apparent temperature calculation."""
    temperature = np.array([[90, 90, 70], [20, 20, 60]]) * units.degF
    rel_humidity = np.array([[60, 20, 60], [10, 10, 10]]) * units.percent
    wind = np.array([[5, 3, 3], [10, 1, 10]]) * units.mph
    truth = np.array([[99.6777178, 90, 70], [8.8140662, 20, 60]]) * units.degF
    res = apparent_temperature(temperature, rel_humidity, wind)
    assert_array_almost_equal(res, truth, 6)
Пример #4
0
def test_apparent_temperature_scalar_no_modification():
    """Test the apparent temperature calculation with a scalar that is NOOP."""
    temperature = 70 * units.degF
    rel_humidity = 60 * units.percent
    wind = 5 * units.mph
    truth = 70 * units.degF
    res = apparent_temperature(temperature, rel_humidity, wind)
    assert_almost_equal(res, truth, 6)
Пример #5
0
def test_apparent_temperature_scalar():
    """Test the apparent temperature calculation with a scalar."""
    temperature = 90 * units.degF
    rel_humidity = 60 * units.percent
    wind = 5 * units.mph
    truth = 99.6777178 * units.degF
    res = apparent_temperature(temperature, rel_humidity, wind)
    assert_almost_equal(res, truth, 6)
Пример #6
0
def test_apparent_temperature_mask_undefined_false():
    """Test that apparent temperature works when mask_undefined is False."""
    temp = np.array([80, 55, 10]) * units.degF
    rh = np.array([40, 50, 25]) * units.percent
    wind = np.array([5, 4, 10]) * units('m/s')

    app_temperature = apparent_temperature(temp, rh, wind, mask_undefined=False)
    assert not hasattr(app_temperature, 'mask')
Пример #7
0
def test_apparent_temperature_windchill():
    """Test that apparent temperature works when a windchill is calculated."""
    temperature = -5. * units.degC
    rel_humidity = 50. * units.percent
    wind = 35. * units('m/s')
    truth = -18.9357 * units.degC
    res = apparent_temperature(temperature, rel_humidity, wind)
    assert_almost_equal(res, truth, 0)
Пример #8
0
def test_apparent_temperature_scalar_no_modification():
    """Test the apparent temperature calculation with a scalar that is NOOP."""
    temperature = 70 * units.degF
    rel_humidity = 60 * units.percent
    wind = 5 * units.mph
    truth = 70 * units.degF
    res = apparent_temperature(temperature, rel_humidity, wind)
    assert_almost_equal(res, truth, 6)
Пример #9
0
def test_apparent_temperature_scalar():
    """Test the apparent temperature calculation with a scalar."""
    temperature = 90 * units.degF
    rel_humidity = 60 * units.percent
    wind = 5 * units.mph
    truth = 99.6777178 * units.degF
    res = apparent_temperature(temperature, rel_humidity, wind)
    assert_almost_equal(res, truth, 6)
Пример #10
0
def test_apparent_temperature_mask_undefined_true():
    """Test that apparent temperature works when mask_undefined is True."""
    temp = np.array([80, 55, 10]) * units.degF
    rh = np.array([40, 50, 25]) * units.percent
    wind = np.array([5, 4, 10]) * units('m/s')

    app_temperature = apparent_temperature(temp, rh, wind, mask_undefined=True)
    mask = [False, True, False]
    assert_array_equal(app_temperature.mask, mask)
Пример #11
0
def test_apparent_temperature():
    """Test the apparent temperature calculation."""
    temperature = np.array([[90, 90, 70], [20, 20, 60]]) * units.degF
    rel_humidity = np.array([[60, 20, 60], [10, 10, 10]]) * units.percent
    wind = np.array([[5, 3, 3], [10, 1, 10]]) * units.mph
    truth = units.Quantity(
        np.ma.array([[99.6777178, 86.3357671, 70], [8.8140662, 20, 60]],
                    mask=[[False, False, True], [False, True, True]]),
        units.degF)
    res = apparent_temperature(temperature, rel_humidity, wind)
    assert_array_almost_equal(res, truth, 6)
Пример #12
0
def test_apparent_temperature():
    """Test the apparent temperature calculation."""
    temperature = np.array([[90, 90, 70],
                            [20, 20, 60]]) * units.degF
    rel_humidity = np.array([[60, 20, 60],
                             [10, 10, 10]]) * units.percent
    wind = np.array([[5, 3, 3],
                     [10, 1, 10]]) * units.mph
    truth = np.array([[99.6777178, 90, 70],
                      [8.8140662, 20, 60]]) * units.degF
    res = apparent_temperature(temperature, rel_humidity, wind)
    assert_array_almost_equal(res, truth, 6)
Пример #13
0
 def calc(self):
     """Compute things not usually computed"""
     if self.data["relh"] is None and None not in [
             self.data["tmpf"],
             self.data["dwpf"],
     ]:
         self.data["relh"] = bounded(
             mcalc.relative_humidity_from_dewpoint(
                 self.data["tmpf"] * munits.degF,
                 self.data["dwpf"] * munits.degF,
             ).to(munits.percent).magnitude,
             0.5,
             100.5,
         )
     if (self.data["dwpf"] is None
             and None not in [self.data["tmpf"], self.data["relh"]]
             and self.data["relh"] >= 1 and self.data["relh"] <= 100):
         self.data["dwpf"] = bounded(
             mcalc.dewpoint_from_relative_humidity(
                 self.data["tmpf"] * munits.degF,
                 self.data["relh"] * munits.percent,
             ).to(munits.degF).magnitude,
             -100.0,
             100.0,
         )
     if self.data["feel"] is None and None not in [
             self.data["tmpf"],
             self.data["relh"],
             self.data["sknt"],
     ]:
         self.data["feel"] = bounded(
             mcalc.apparent_temperature(
                 self.data["tmpf"] * munits.degF,
                 self.data["relh"] * munits.percent,
                 self.data["sknt"] * munits.knots,
             ).to(munits.degF).magnitude,
             -150.0,
             200.0,
         )
Пример #14
0
 def calc(self):
     """Compute things not usually computed"""
     if (self.data['relh'] is None
             and None not in [self.data['tmpf'], self.data['dwpf']]):
         self.data['relh'] = bounded(
             mcalc.relative_humidity_from_dewpoint(
                 self.data['tmpf'] * munits.degF, self.data['dwpf'] *
                 munits.degF).to(munits.percent).magnitude, 0.5, 100.5)
     if (self.data['dwpf'] is None
             and None not in [self.data['tmpf'], self.data['relh']]
             and self.data['relh'] >= 1 and self.data['relh'] <= 100):
         self.data['dwpf'] = bounded(
             mcalc.dewpoint_rh(self.data['tmpf'] * munits.degF,
                               self.data['relh'] * munits.percent).to(
                                   munits.degF).magnitude, -100., 100.)
     if (self.data['feel'] is None and None not in [
             self.data['tmpf'], self.data['relh'], self.data['sknt']
     ]):
         self.data['feel'] = bounded(
             mcalc.apparent_temperature(
                 self.data['tmpf'] * munits.degF,
                 self.data['relh'] * munits.percent, self.data['sknt'] *
                 munits.knots).to(munits.degF).magnitude, -150., 200.)
Пример #15
0
def do(ts):
    """Process this date timestamp"""
    asos = get_dbconn('asos', user='******')
    iemaccess = get_dbconn('iem')
    icursor = iemaccess.cursor()
    df = read_sql("""
    select station, network, iemid, drct, sknt,
    valid at time zone tzname as localvalid,
    tmpf, dwpf from
    alldata d JOIN stations t on (t.id = d.station)
    where (network ~* 'ASOS' or network = 'AWOS')
    and valid between %s and %s and t.tzname is not null
    and date(valid at time zone tzname) = %s
    ORDER by valid ASC
    """, asos, params=(ts - datetime.timedelta(days=2),
                       ts + datetime.timedelta(days=2),
                       ts.strftime("%Y-%m-%d")), index_col=None)
    # derive some parameters
    df['relh'] = mcalc.relative_humidity_from_dewpoint(
        df['tmpf'].values * munits.degF,
        df['dwpf'].values * munits.degF).to(munits.percent)
    df['feel'] = mcalc.apparent_temperature(
        df['tmpf'].values * munits.degF,
        df['relh'].values * munits.percent,
        df['sknt'].values * munits.knots
    )
    df['u'], df['v'] = mcalc.get_wind_components(
        df['sknt'].values * munits.knots,
        df['drct'].values * munits.deg
    )
    df['localvalid_lag'] = df.groupby('iemid')['localvalid'].shift(1)
    df['timedelta'] = df['localvalid'] - df['localvalid_lag']
    ndf = df[pd.isna(df['timedelta'])]
    df.loc[ndf.index.values, 'timedelta'] = pd.to_timedelta(
            ndf['localvalid'].dt.hour * 3600. +
            ndf['localvalid'].dt.minute * 60., unit='s'
    )
    df['timedelta'] = df['timedelta'] / np.timedelta64(1, 's')

    table = "summary_%s" % (ts.year,)
    for iemid, gdf in df.groupby('iemid'):
        if len(gdf.index) < 6:
            # print(" Quorum not meet for %s" % (gdf.iloc[0]['station'], ))
            continue
        ldf = gdf.copy()
        ldf.interpolate(inplace=True)
        totsecs = ldf['timedelta'].sum()
        avg_rh = clean((ldf['relh'] * ldf['timedelta']).sum() / totsecs, 1,
                       100)
        min_rh = clean(ldf['relh'].min(), 1, 100)
        max_rh = clean(ldf['relh'].max(), 1, 100)

        uavg = (ldf['u'] * ldf['timedelta']).sum() / totsecs
        vavg = (ldf['u'] * ldf['timedelta']).sum() / totsecs
        drct = clean(
            mcalc.get_wind_dir(uavg * munits.knots, vavg * munits.knots),
            0, 360)
        avg_sknt = clean(
            (ldf['sknt'] * ldf['timedelta']).sum() / totsecs, 0, 150  # arb
        )
        max_feel = clean(ldf['feel'].max(), -150, 200)
        avg_feel = clean(
            (ldf['feel'] * ldf['timedelta']).sum() / totsecs, -150, 200
        )
        min_feel = clean(ldf['feel'].min(), -150, 200)

        def do_update():
            """Inline updating"""
            icursor.execute("""
            UPDATE """ + table + """
            SET avg_rh = %s, min_rh = %s, max_rh = %s,
            avg_sknt = %s, vector_avg_drct = %s,
            min_feel = %s, avg_feel = %s, max_feel = %s
            WHERE
            iemid = %s and day = %s
            """, (avg_rh, min_rh, max_rh, avg_sknt, drct,
                  min_feel, avg_feel, max_feel,
                  iemid, ts))
        do_update()
        if icursor.rowcount == 0:
            print(('compute_daily Adding %s for %s %s %s'
                   ) % (table, gdf.iloc[0]['station'], gdf.iloc[0]['network'],
                        ts))
            icursor.execute("""
                INSERT into """ + table + """
                (iemid, day) values (%s, %s)
            """, (iemid, ts))
            do_update()

    icursor.close()
    iemaccess.commit()
    iemaccess.close()
Пример #16
0
def process_metar(mstr, now):
    """ Do the METAR Processing """
    mtr = None
    while mtr is None:
        try:
            mtr = Metar(mstr, now.month, now.year)
        except MetarParserError as exp:
            try:
                msg = str(exp)
            except Exception as exp:
                return None
            tokens = ERROR_RE.findall(str(exp))
            orig_mstr = mstr
            if tokens:
                for token in tokens[0].split():
                    mstr = mstr.replace(" %s" % (token, ), "")
                if orig_mstr == mstr:
                    print("Can't fix badly formatted metar: " + mstr)
                    return None
            else:
                print("MetarParserError: "+msg)
                return None
        except Exception as exp:
            print("Double Fail: %s %s" % (mstr, exp))
            return None
    if mtr is None or mtr.time is None:
        return None

    ob = OB()
    ob.metar = mstr[:254]
    ob.valid = now

    if mtr.temp:
        ob.tmpf = mtr.temp.value("F")
    if mtr.dewpt:
        ob.dwpf = mtr.dewpt.value("F")

    if mtr.wind_speed:
        ob.sknt = mtr.wind_speed.value("KT")
    if mtr.wind_gust:
        ob.gust = mtr.wind_gust.value("KT")

    # Calc some stuff
    if ob.tmpf is not None and ob.dwpf is not None:
        ob.relh = relative_humidity_from_dewpoint(
            ob.tmpf * units('degF'),
            ob.dwpf * units('degF')
        ).to(units('percent')).magnitude
        if ob.sknt is not None:
            ob.feel = apparent_temperature(
                ob.tmpf * units('degF'),
                ob.relh * units('percent'),
                ob.sknt * units('knots')
            ).to(units('degF')).magnitude

    if mtr.wind_dir and mtr.wind_dir.value() != "VRB":
        ob.drct = mtr.wind_dir.value()

    if mtr.vis:
        ob.vsby = mtr.vis.value("SM")

    # see pull request #38
    if mtr.press and mtr.press != mtr.press_sea_level:
        ob.alti = mtr.press.value("IN")

    if mtr.press_sea_level:
        ob.mslp = mtr.press_sea_level.value("MB")

    if mtr.precip_1hr:
        ob.p01i = mtr.precip_1hr.value("IN")

    # Do something with sky coverage
    for i in range(len(mtr.sky)):
        (c, h, _) = mtr.sky[i]
        setattr(ob, 'skyc%s' % (i+1), c)
        if h is not None:
            setattr(ob, 'skyl%s' % (i+1), h.value("FT"))

    if mtr.max_temp_6hr:
        ob.max_tmpf_6hr = mtr.max_temp_6hr.value("F")
    if mtr.min_temp_6hr:
        ob.min_tmpf_6hr = mtr.min_temp_6hr.value("F")
    if mtr.max_temp_24hr:
        ob.max_tmpf_24hr = mtr.max_temp_24hr.value("F")
    if mtr.min_temp_24hr:
        ob.min_tmpf_6hr = mtr.min_temp_24hr.value("F")
    if mtr.precip_3hr:
        ob.p03i = mtr.precip_3hr.value("IN")
    if mtr.precip_6hr:
        ob.p06i = mtr.precip_6hr.value("IN")
    if mtr.precip_24hr:
        ob.p24i = mtr.precip_24hr.value("IN")

    # Presentwx
    if mtr.weather:
        pwx = []
        for wx in mtr.weather:
            val = "".join([a for a in wx if a is not None])
            if val == "" or val == len(val) * "/":
                continue
            pwx.append(val)
        ob.wxcodes = pwx

    return ob
Пример #17
0
T2M = T2M_DATA.variables['air'][TIME_INDEX, :, :] * units('kelvin')
U2M = U2M_DATA.variables['uwnd'][TIME_INDEX, :, :] * units('m/s')
V2M = V2M_DATA.variables['vwnd'][TIME_INDEX, :, :] * units('m/s')
# =============================================================================
# FIG #8: SFC: HEAT INDEX OR WINDCHILL
# =============================================================================
T2M = T2M_DATA.variables['air'][TIME_INDEX, :, :] * units('kelvin')
SH2M = SH2M_DATA.variables['shum'][TIME_INDEX, :, :]
U2M = U2M_DATA.variables['uwnd'][TIME_INDEX, :, :] * units('m/s')
V2M = V2M_DATA.variables['vwnd'][TIME_INDEX, :, :] * units('m/s')
PRES = PRES_DATA.variables['pres'][TIME_INDEX, :, :] * units('Pa')
RHUM = mpcalc.relative_humidity_from_specific_humidity(SH2M, T2M, PRES)
T2M = T2M.to('degF')
SFC_SPEED = mpcalc.get_wind_speed(U2M, V2M)
SFC_SPEED = SFC_SPEED.to('mph')
APPARENT_TEMP = mpcalc.apparent_temperature(T2M, RHUM, SFC_SPEED)
# =============================================================================
# =============================================================================
# =============================================================================
# Make a grid of lat/lon values to use for plotting with Basemap.
lons, lats = np.meshgrid(np.squeeze(LON), np.squeeze(LAT))
slons, slats = np.meshgrid(np.squeeze(SFLON), np.squeeze(SFLAT))
fig, axarr = plt.subplots(nrows=2,
                          ncols=4,
                          figsize=(35, 25),
                          subplot_kw={'projection': crs})
axlist = axarr.flatten()
fig.tight_layout()
for ax in axlist:
    plot_background(ax)
# =============================================================================
Пример #18
0
def plotter(fdict):
    """ Go """
    ctx = get_autoplot_context(fdict, get_description())
    varname = ctx["v"]

    if ctx["t"] == "state":
        bnds = reference.state_bounds[ctx["state"]]
        title = reference.state_names[ctx["state"]]
    else:
        bnds = reference.wfo_bounds[ctx["wfo"]]
        title = "NWS CWA %s [%s]" % (
            ctx["_nt"].sts[ctx["wfo"]]["name"],
            ctx["wfo"],
        )
    df, valid = get_df(ctx, bnds)
    if df.empty:
        raise NoDataFound("No data was found for your query")
    mp = MapPlot(
        sector=("state" if ctx["t"] == "state" else "cwa"),
        state=ctx["state"],
        cwa=(ctx["wfo"] if len(ctx["wfo"]) == 3 else ctx["wfo"][1:]),
        axisbg="white",
        title="%s for %s" % (PDICT2[ctx["v"]], title),
        subtitle=("Map valid: %s UTC") % (valid.strftime("%d %b %Y %H:%M"),),
        nocaption=True,
        titlefontsize=16,
    )
    if varname == "vsby":
        ramp = np.array([0.01, 0.1, 0.25, 0.5, 1, 2, 3, 5, 8, 9.9])
        valunit = "miles"
    elif varname == "feel":
        valunit = "F"
        df["feel"] = (
            apparent_temperature(
                df["tmpf"].values * units("degF"),
                df["relh"].values * units("percent"),
                df["sknt"].values * units("knots"),
            )
            .to(units("degF"))
            .m
        )
    # Data QC, cough
    if ctx.get("above"):
        df = df[df[varname] < ctx["above"]]
    if ctx.get("below"):
        df = df[df[varname] > ctx["below"]]
    # with QC done, we compute ramps
    if varname != "vsby":
        ramp = np.linspace(
            df[varname].min() - 5, df[varname].max() + 5, 10, dtype="i"
        )

    mp.contourf(
        df["lon"].values,
        df["lat"].values,
        df[varname].values,
        ramp,
        units=valunit,
        cmap=get_cmap(ctx["cmap"]),
    )
    if ctx["t"] == "state":
        df2 = df[df["state"] == ctx["state"]]
    else:
        df2 = df[df["wfo"] == ctx["wfo"]]

    mp.plot_values(
        df2["lon"].values,
        df2["lat"].values,
        df2[varname].values,
        "%.1f",
        labelbuffer=10,
    )
    mp.drawcounties()
    if ctx["t"] == "cwa":
        mp.draw_cwas()

    return mp.fig, df
Пример #19
0
def process_metar(mstr, now):
    """ Do the METAR Processing """
    mtr = None
    while mtr is None:
        try:
            mtr = Metar(mstr, now.month, now.year)
        except MetarParserError as exp:
            try:
                msg = str(exp)
            except Exception as exp:
                return None
            tokens = ERROR_RE.findall(str(exp))
            orig_mstr = mstr
            if tokens:
                for token in tokens[0].split():
                    mstr = mstr.replace(" %s" % (token, ), "")
                if orig_mstr == mstr:
                    print("Can't fix badly formatted metar: " + mstr)
                    return None
            else:
                print("MetarParserError: " + msg)
                return None
        except Exception as exp:
            print("Double Fail: %s %s" % (mstr, exp))
            return None
    if mtr is None or mtr.time is None:
        return None

    ob = OB()
    ob.metar = mstr[:254]
    ob.valid = now

    if mtr.temp:
        ob.tmpf = mtr.temp.value("F")
    if mtr.dewpt:
        ob.dwpf = mtr.dewpt.value("F")

    if mtr.wind_speed:
        ob.sknt = mtr.wind_speed.value("KT")
    if mtr.wind_gust:
        ob.gust = mtr.wind_gust.value("KT")

    # Calc some stuff
    if ob.tmpf is not None and ob.dwpf is not None:
        ob.relh = relative_humidity_from_dewpoint(
            ob.tmpf * units('degF'),
            ob.dwpf * units('degF')).to(units('percent')).magnitude
        if ob.sknt is not None:
            ob.feel = apparent_temperature(ob.tmpf * units('degF'),
                                           ob.relh * units('percent'),
                                           ob.sknt * units('knots')).to(
                                               units('degF')).magnitude

    if mtr.wind_dir and mtr.wind_dir.value() != "VRB":
        ob.drct = mtr.wind_dir.value()

    if mtr.vis:
        ob.vsby = mtr.vis.value("SM")

    # see pull request #38
    if mtr.press and mtr.press != mtr.press_sea_level:
        ob.alti = mtr.press.value("IN")

    if mtr.press_sea_level:
        ob.mslp = mtr.press_sea_level.value("MB")

    if mtr.precip_1hr:
        ob.p01i = mtr.precip_1hr.value("IN")

    # Do something with sky coverage
    for i in range(len(mtr.sky)):
        (c, h, _) = mtr.sky[i]
        setattr(ob, 'skyc%s' % (i + 1), c)
        if h is not None:
            setattr(ob, 'skyl%s' % (i + 1), h.value("FT"))

    if mtr.max_temp_6hr:
        ob.max_tmpf_6hr = mtr.max_temp_6hr.value("F")
    if mtr.min_temp_6hr:
        ob.min_tmpf_6hr = mtr.min_temp_6hr.value("F")
    if mtr.max_temp_24hr:
        ob.max_tmpf_24hr = mtr.max_temp_24hr.value("F")
    if mtr.min_temp_24hr:
        ob.min_tmpf_6hr = mtr.min_temp_24hr.value("F")
    if mtr.precip_3hr:
        ob.p03i = mtr.precip_3hr.value("IN")
    if mtr.precip_6hr:
        ob.p06i = mtr.precip_6hr.value("IN")
    if mtr.precip_24hr:
        ob.p24i = mtr.precip_24hr.value("IN")

    # Presentwx
    if mtr.weather:
        pwx = []
        for wx in mtr.weather:
            val = "".join([a for a in wx if a is not None])
            if val == "" or val == len(val) * "/":
                continue
            pwx.append(val)
        ob.wxcodes = pwx

    return ob
Пример #20
0
def plotter(fdict):
    """ Go """
    ctx = get_autoplot_context(fdict, get_description())
    varname = ctx['v']

    if ctx['t'] == 'state':
        bnds = reference.state_bounds[ctx['state']]
        title = reference.state_names[ctx['state']]
    else:
        bnds = reference.wfo_bounds[ctx['wfo']]
        title = "NWS CWA %s [%s]" % (ctx['_nt'].sts[ctx['wfo']]['name'],
                                     ctx['wfo'])
    df, valid = get_df(ctx, bnds)
    if df.empty:
        raise NoDataFound("No data was found for your query")
    mp = MapPlot(sector=('state' if ctx['t'] == 'state' else 'cwa'),
                 state=ctx['state'],
                 cwa=(ctx['wfo'] if len(ctx['wfo']) == 3 else ctx['wfo'][1:]),
                 axisbg='white',
                 title='%s for %s' % (PDICT2[ctx['v']], title),
                 subtitle=('Map valid: %s UTC') %
                 (valid.strftime("%d %b %Y %H:%M"), ),
                 nocaption=True,
                 titlefontsize=16)
    if varname == 'vsby':
        ramp = np.array([0.01, 0.1, 0.25, 0.5, 1, 2, 3, 5, 8, 9.9])
        valunit = 'miles'
    elif varname == 'feel':
        valunit = 'F'
        df['feel'] = apparent_temperature(df['tmpf'].values * units('degF'),
                                          df['relh'].values * units('percent'),
                                          df['sknt'].values *
                                          units('knots')).to(units('degF')).m
    # Data QC, cough
    if ctx.get('above'):
        df = df[df[varname] < ctx['above']]
    if ctx.get('below'):
        df = df[df[varname] > ctx['below']]
    # with QC done, we compute ramps
    if varname != 'vsby':
        ramp = np.linspace(df[varname].min() - 5,
                           df[varname].max() + 5,
                           10,
                           dtype='i')

    mp.contourf(df['lon'].values,
                df['lat'].values,
                df[varname].values,
                ramp,
                units=valunit,
                cmap=plt.get_cmap(ctx['cmap']))
    if ctx['t'] == 'state':
        df2 = df[df['state'] == ctx['state']]
    else:
        df2 = df[df['wfo'] == ctx['wfo']]

    mp.plot_values(df2['lon'].values,
                   df2['lat'].values,
                   df2[varname].values,
                   '%.1f',
                   labelbuffer=10)
    mp.drawcounties()
    if ctx['t'] == 'cwa':
        mp.draw_cwas()

    return mp.fig, df