Пример #1
0
    def avgWinds(self):
        if not self.aSknt:
            self.sped = None
            self.drct = None
            return

        self.avg_sknt = int(float(sum(self.aSknt)) / float(len(self.aSknt)))
        utot = 0
        vtot = 0
        for s, d in zip(self.aSknt, self.aDrct):
            u, v = uv(s, d)
            if s > self.xsped:
                self.xsped = s * 1.150
                self.xdrct = d
                self.xdrctTxt = util.drct2text(d)

            utot += u
            vtot += v
        uavg = utot / len(self.aSknt)
        vavg = vtot / len(self.aSknt)
        self.avg_drct = mydir(uavg, vavg)
        self.avg_drctTxt = util.drct2text(self.avg_drct)

        self.aSknt = []
        self.aDrct = []
Пример #2
0
def test_drct2text():
    """ Test conversion of drct2text """
    assert util.drct2text(360) == "N"
    assert util.drct2text(90) == "E"
    # A hack to get move coverage
    for i in range(360):
        util.drct2text(i)
Пример #3
0
    def avgWinds(self):
        if not self.aSknt:
            self.sped = None
            self.drct = None
            return

        self.avg_sknt = int(float(sum(self.aSknt)) / float(len(self.aSknt)))
        utot = 0
        vtot = 0
        for s, d in zip(self.aSknt, self.aDrct):
            u, v = uv(s, d)
            if s > self.xsped:
                self.xsped = s * 1.150
                self.xdrct = d
                self.xdrctTxt = util.drct2text(d)

            utot += u
            vtot += v
        uavg = utot / len(self.aSknt)
        vavg = vtot / len(self.aSknt)
        self.avg_drct = mydir(uavg, vavg)
        self.avg_drctTxt = util.drct2text(self.avg_drct)

        self.aSknt = []
        self.aDrct = []
Пример #4
0
def computeOthers(d):
    r = {}
    # Need something to compute other values needed for output
    for sid in d.keys():
        ob = d[sid]
        ob["ticks"] = calendar.timegm(ob['utc_valid'].timetuple())
        if ob['sknt'] is not None:
            ob["sped"] = ob["sknt"] * 1.17
        if ob.get('tmpf') is not None and ob.get('dwpf') is not None:
            tmpf = temperature(ob['tmpf'], 'F')
            dwpf = temperature(ob['dwpf'], 'F')
            ob["relh"] = meteorology.relh(tmpf, dwpf).value('%')
        else:
            ob['relh'] = None
        if ob['relh'] == 'M':
            ob['relh'] = None

        if (ob.get('tmpf') is not None and ob.get('dwpf') is not None and
                ob.get('sped') is not None):
            ob['feel'] = meteorology.mcalc_feelslike(
                masked_array([ob['tmpf'], ], units('degF'), mask=[False, ]),
                masked_array([ob['dwpf'], ],  units('degF'), mask=[False, ]),
                masked_array([ob['sped'], ], units('mile per hour'),
                             mask=[False, ])
                ).to(units('degF')).magnitude[0]
        else:
            ob['feel'] = None
        if ob['feel'] == 'M':
            ob['feel'] = None

        ob["altiTend"] = 'S'
        ob["drctTxt"] = util.drct2text(ob["drct"])
        if ob["max_drct"] is None:
            ob["max_drct"] = 0
        ob["max_drctTxt"] = util.drct2text(ob["max_drct"])
        ob["20gu"] = 0
        if ob['gust'] is not None:
            ob["gmph"] = ob["gust"] * 1.17
        if ob['max_gust'] is not None:
            ob["max_sped"] = ob["max_gust"] * 1.17
        else:
            ob['max_sped'] = 0
        ob['pday'] = 0 if ob['pday'] is None else ob['pday']
        ob['pmonth'] = 0 if ob['pmonth'] is None else ob['pmonth']
        ob["gtim"] = "0000"
        ob["gtim2"] = "12:00 AM"
        if ob["max_gust_ts"] is not None and ob["max_gust_ts"] != "null":
            ob["gtim"] = ob["max_gust_ts"].strftime("%H%M")
            ob["gtim2"] = ob["max_gust_ts"].strftime("%-I:%M %p")
        r[sid] = ob
    return r
Пример #5
0
def computeOthers(d):
    r = {}
    # Need something to compute other values needed for output
    for sid in d.keys():
        ob = d[sid]
        ob["ticks"] = calendar.timegm(ob['utc_valid'].timetuple())
        if ob['sknt'] is not None:
            ob["sped"] = ob["sknt"] * 1.17
        if ob.get('tmpf') is not None and ob.get('dwpf') is not None:
            tmpf = temperature(ob['tmpf'], 'F')
            dwpf = temperature(ob['dwpf'], 'F')
            ob["relh"] = meteorology.relh(tmpf, dwpf).value('%')
        else:
            ob['relh'] = None
        if ob['relh'] == 'M':
            ob['relh'] = None

        if (ob.get('tmpf') is not None and ob.get('dwpf') is not None and
                ob.get('sped') is not None):
            tmpf = temperature(ob['tmpf'], 'F')
            dwpf = temperature(ob['dwpf'], 'F')
            sknt = speed(ob['sped'], 'MPH')
            ob["feel"] = meteorology.feelslike(tmpf, dwpf, sknt).value("F")
        else:
            ob['feel'] = None
        if ob['feel'] == 'M':
            ob['feel'] = None

        ob["altiTend"] = 'S'
        ob["drctTxt"] = util.drct2text(ob["drct"])
        if ob["max_drct"] is None:
            ob["max_drct"] = 0
        ob["max_drctTxt"] = util.drct2text(ob["max_drct"])
        ob["20gu"] = 0
        if ob['gust'] is not None:
            ob["gmph"] = ob["gust"] * 1.17
        if ob['max_gust'] is not None:
            ob["max_sped"] = ob["max_gust"] * 1.17
        else:
            ob['max_sped'] = 0
        ob['pday'] = 0 if ob['pday'] is None else ob['pday']
        ob['pmonth'] = 0 if ob['pmonth'] is None else ob['pmonth']
        ob["gtim"] = "0000"
        ob["gtim2"] = "12:00 AM"
        if ob["max_gust_ts"] is not None and ob["max_gust_ts"] != "null":
            ob["gtim"] = ob["max_gust_ts"].strftime("%H%M")
            ob["gtim2"] = ob["max_gust_ts"].strftime("%-I:%M %p")
        r[sid] = ob
    return r
Пример #6
0
 def wind_message(self):
     """Convert this into a Jabber style message"""
     drct = 0
     sknt = 0
     time = self.time.replace(tzinfo=timezone.utc)
     if self.wind_gust:
         sknt = self.wind_gust.value("KT")
         if self.wind_dir:
             drct = self.wind_dir.value()
     if self.wind_speed_peak:
         v1 = self.wind_speed_peak.value("KT")
         d1 = self.wind_dir_peak.value()
         t1 = self.peak_wind_time.replace(tzinfo=timezone.utc)
         if v1 > sknt:
             sknt = v1
             drct = d1
             time = t1
     key = "%s;%s;%s" % (self.station_id, sknt, time)
     if key not in WIND_ALERTS:
         WIND_ALERTS[key] = 1
         speed = datatypes.speed(sknt, "KT")
         return ("gust of %.0f knots (%.1f mph) from %s @ %s") % (
             speed.value("KT"),
             speed.value("MPH"),
             drct2text(drct),
             time.strftime("%H%MZ"),
         )
Пример #7
0
Файл: p71.py Проект: akrherz/iem
def plotter(fdict):
    """ Go """
    import matplotlib
    matplotlib.use('agg')
    import matplotlib.pyplot as plt
    import matplotlib.patheffects as PathEffects
    pgconn = psycopg2.connect(database='iem', host='iemdb', user='******')
    cursor = pgconn.cursor(cursor_factory=psycopg2.extras.DictCursor)

    station = fdict.get('zstation', 'AMW')
    network = fdict.get('network', 'IA_ASOS')
    units = fdict.get('units', 'MPH').upper()
    if units not in PDICT:
        units = 'MPH'
    year = int(fdict.get('year', datetime.datetime.now().year))
    month = int(fdict.get('month', datetime.datetime.now().month))
    sts = datetime.date(year, month, 1)
    ets = (sts + datetime.timedelta(days=35)).replace(day=1)
    nt = NetworkTable(network)

    cursor.execute("""
      SELECT day, avg_sknt, vector_avg_drct from summary s JOIN stations t
      ON (t.iemid = s.iemid) WHERE t.id = %s and t.network = %s and
      s.day >= %s and s.day < %s ORDER by day ASC
    """, (station, network, sts, ets))
    days = []
    drct = []
    sknt = []
    for row in cursor:
        if row[1] is None:
            continue
        days.append(row[0].day)
        drct.append(row[2])
        sknt.append(row[1])
    if len(sknt) == 0:
        return "ERROR: No Data Found"
    df = pd.DataFrame(dict(day=pd.Series(days),
                           drct=pd.Series(drct),
                           sknt=pd.Series(sknt)))
    sknt = speed(np.array(sknt), 'KT').value(units)
    (fig, ax) = plt.subplots(1, 1)
    ax.bar(np.array(days)-0.4, sknt, ec='green', fc='green')
    pos = max([min(sknt) / 2.0, 0.5])
    for d, _, r in zip(days, sknt, drct):
        draw_line(plt, d, max(sknt)+0.5, (270. - r) / 180. * np.pi)
        txt = ax.text(d, pos, drct2text(r), ha='center', rotation=90,
                      color='white', va='center')
        txt.set_path_effects([PathEffects.withStroke(linewidth=2,
                                                     foreground="k")])
    ax.grid(True, zorder=11)
    ax.set_title(("%s [%s]\n%s Daily Average Wind Speed and Direction"
                  ) % (nt.sts[station]['name'], station,
                       sts.strftime("%b %Y")))
    ax.set_xlim(0.5, max(days)+0.5)
    ax.set_ylim(top=max(sknt)+2)

    ax.set_ylabel("Average Wind Speed [%s]" % (PDICT.get(units),))

    return fig, df
Пример #8
0
def plotter(fdict):
    """ Go """
    pgconn = get_dbconn('iem')
    cursor = pgconn.cursor(cursor_factory=psycopg2.extras.DictCursor)
    ctx = get_autoplot_context(fdict, get_description())
    station = ctx['zstation']
    network = ctx['network']
    units = ctx['units']
    year = ctx['year']
    month = ctx['month']
    sts = datetime.date(year, month, 1)
    ets = (sts + datetime.timedelta(days=35)).replace(day=1)
    nt = NetworkTable(network)

    cursor.execute(
        """
      SELECT day, avg_sknt, vector_avg_drct from summary s JOIN stations t
      ON (t.iemid = s.iemid) WHERE t.id = %s and t.network = %s and
      s.day >= %s and s.day < %s ORDER by day ASC
    """, (station, network, sts, ets))
    days = []
    drct = []
    sknt = []
    for row in cursor:
        if row[1] is None:
            continue
        days.append(row[0].day)
        drct.append(row[2])
        sknt.append(row[1])
    if not sknt:
        raise ValueError("ERROR: No Data Found")
    df = pd.DataFrame(
        dict(day=pd.Series(days), drct=pd.Series(drct), sknt=pd.Series(sknt)))
    sknt = speed(np.array(sknt), 'KT').value(units)
    (fig, ax) = plt.subplots(1, 1)
    ax.bar(np.array(days), sknt, ec='green', fc='green', align='center')
    pos = max([min(sknt) / 2.0, 0.5])
    for d, _, r in zip(days, sknt, drct):
        draw_line(d, max(sknt) + 0.5, (270. - r) / 180. * np.pi)
        txt = ax.text(d,
                      pos,
                      drct2text(r),
                      ha='center',
                      rotation=90,
                      color='white',
                      va='center')
        txt.set_path_effects(
            [PathEffects.withStroke(linewidth=2, foreground="k")])
    ax.grid(True, zorder=11)
    ax.set_title(("%s [%s]\n%s Daily Average Wind Speed and Direction") %
                 (nt.sts[station]['name'], station, sts.strftime("%b %Y")))
    ax.set_xlim(0.5, 31.5)
    ax.set_xticks(range(1, 31, 5))
    ax.set_ylim(top=max(sknt) + 2)

    ax.set_ylabel("Average Wind Speed [%s]" % (PDICT.get(units), ))

    return fig, df
Пример #9
0
def plotter(fdict):
    """ Go """
    pgconn = get_dbconn("iem")
    ctx = get_autoplot_context(fdict, get_description())
    station = ctx["zstation"]
    plot_units = ctx["units"]
    year = ctx["year"]
    month = ctx["month"]
    sts = datetime.date(year, month, 1)
    ets = (sts + datetime.timedelta(days=35)).replace(day=1)

    df = read_sql(
        """
        SELECT day, avg_sknt as sknt, vector_avg_drct as drct
        from summary s JOIN stations t
        ON (t.iemid = s.iemid) WHERE t.id = %s and t.network = %s and
        s.day >= %s and s.day < %s and avg_sknt is not null
        ORDER by day ASC
    """,
        pgconn,
        params=(station, ctx["network"], sts, ets),
    )
    if df.empty:
        raise NoDataFound("ERROR: No Data Found")
    df["day"] = pd.to_datetime(df["day"])
    sknt = (df["sknt"].values * units("knot")).to(XREF_UNITS[plot_units]).m
    (fig, ax) = plt.subplots(1, 1)
    ax.bar(df["day"].dt.day.values,
           sknt,
           ec="green",
           fc="green",
           align="center")
    pos = max([min(sknt) / 2.0, 0.5])
    for d, _, r in zip(df["day"].dt.day.values, sknt, df["drct"].values):
        draw_line(d, max(sknt) + 0.5, (270.0 - r) / 180.0 * np.pi)
        txt = ax.text(
            d,
            pos,
            drct2text(r),
            ha="center",
            rotation=90,
            color="white",
            va="center",
        )
        txt.set_path_effects(
            [PathEffects.withStroke(linewidth=2, foreground="k")])
    ax.grid(True, zorder=11)
    ax.set_title(
        ("%s [%s]\n%s Daily Average Wind Speed and Direction") %
        (ctx["_nt"].sts[station]["name"], station, sts.strftime("%b %Y")))
    ax.set_xlim(0.5, 31.5)
    ax.set_xticks(range(1, 31, 5))
    ax.set_ylim(top=max(sknt) + 2)

    ax.set_ylabel("Average Wind Speed [%s]" % (PDICT[plot_units], ))

    return fig, df
Пример #10
0
def plotter(fdict):
    """ Go """
    import matplotlib
    matplotlib.use('agg')
    import matplotlib.pyplot as plt
    pgconn = psycopg2.connect(database='asos', host='iemdb', user='******')
    ctx = get_autoplot_context(fdict, get_description())

    station = ctx['zstation']
    network = ctx['network']
    units = ctx['units']

    nt = NetworkTable(network)

    df = read_sql("""
        select date_trunc('hour', valid) as ts, avg(sknt) as sknt,
        max(drct) as drct from alldata
        WHERE station = %s and sknt is not null and drct is not null
        GROUP by ts
        """, pgconn, params=(station, ), parse_dates=('ts',),
                  index_col=None)
    sknt = speed(df['sknt'].values, 'KT')
    drct = direction(df['drct'].values, 'DEG')
    df['u'], df['v'] = [x.value('MPS') for x in meteorology.uv(sknt, drct)]
    df['month'] = df['ts'].dt.month
    grp = df[['month', 'u', 'v', 'sknt']].groupby('month').mean()
    grp['u_%s' % (units,)] = speed(grp['u'].values, 'KT').value(units.upper())
    grp['v_%s' % (units,)] = speed(grp['u'].values, 'KT').value(units.upper())
    grp['sped_%s' % (units,)] = speed(grp['sknt'].values,
                                      'KT').value(units.upper())
    drct = meteorology.drct(speed(grp['u'].values, 'KT'),
                            speed(grp['v'].values, 'KT'))
    grp['drct'] = drct.value('DEG')
    maxval = grp['sped_%s' % (units,)].max()
    (fig, ax) = plt.subplots(1, 1)
    ax.barh(grp.index.values, grp['sped_%s' % (units,)].values,
            align='center')
    ax.set_xlabel("Average Wind Speed [%s]" % (UNITS[units],))
    ax.set_yticks(grp.index.values)
    ax.set_yticklabels(calendar.month_abbr[1:])
    ax.grid(True)
    ax.set_xlim(0, maxval * 1.2)
    for mon, row in grp.iterrows():
        ax.text(maxval * 1.1, mon, drct2text(row['drct']), ha='center',
                va='center', bbox=dict(color='white'))
        ax.text(row['sped_%s' % (units,)] * 0.98, mon,
                "%.1f" % (row['sped_%s' % (units,)],), ha='right',
                va='center', bbox=dict(color='white',
                                       boxstyle='square,pad=0.03',))
    ax.set_ylim(12.5, 0.5)
    ax.set_title(("[%s] %s [%s-%s]\nMonthly Average Wind Speed and"
                  " Vector Average Direction"
                  ) % (station, nt.sts[station]['name'],
                       df['ts'].min().year,
                       df['ts'].max().year))

    return fig, grp
Пример #11
0
def plotter(fdict):
    """ Go """
    import matplotlib
    matplotlib.use('agg')
    import matplotlib.pyplot as plt
    pgconn = get_dbconn('asos')
    ctx = get_autoplot_context(fdict, get_description())

    station = ctx['zstation']
    network = ctx['network']
    units = ctx['units']

    nt = NetworkTable(network)

    df = read_sql("""
        select date_trunc('hour', valid at time zone 'UTC') as ts,
        avg(sknt) as sknt, max(drct) as drct from alldata
        WHERE station = %s and sknt is not null and drct is not null
        GROUP by ts
        """, pgconn, params=(station, ), index_col=None)
    sknt = speed(df['sknt'].values, 'KT')
    drct = direction(df['drct'].values, 'DEG')
    df['u'], df['v'] = [x.value('MPS') for x in meteorology.uv(sknt, drct)]
    df['month'] = df['ts'].dt.month
    grp = df[['month', 'u', 'v', 'sknt']].groupby('month').mean()
    grp['u_%s' % (units,)] = speed(grp['u'].values, 'KT').value(units.upper())
    grp['v_%s' % (units,)] = speed(grp['u'].values, 'KT').value(units.upper())
    grp['sped_%s' % (units,)] = speed(grp['sknt'].values,
                                      'KT').value(units.upper())
    drct = meteorology.drct(speed(grp['u'].values, 'KT'),
                            speed(grp['v'].values, 'KT'))
    grp['drct'] = drct.value('DEG')
    maxval = grp['sped_%s' % (units,)].max()
    (fig, ax) = plt.subplots(1, 1)
    ax.barh(grp.index.values, grp['sped_%s' % (units,)].values,
            align='center')
    ax.set_xlabel("Average Wind Speed [%s]" % (UNITS[units],))
    ax.set_yticks(grp.index.values)
    ax.set_yticklabels(calendar.month_abbr[1:])
    ax.grid(True)
    ax.set_xlim(0, maxval * 1.2)
    for mon, row in grp.iterrows():
        ax.text(maxval * 1.1, mon, drct2text(row['drct']), ha='center',
                va='center', bbox=dict(color='white'))
        ax.text(row['sped_%s' % (units,)] * 0.98, mon,
                "%.1f" % (row['sped_%s' % (units,)],), ha='right',
                va='center', bbox=dict(color='white',
                                       boxstyle='square,pad=0.03',))
    ax.set_ylim(12.5, 0.5)
    ax.set_title(("[%s] %s [%s-%s]\nMonthly Average Wind Speed and"
                  " Vector Average Direction"
                  ) % (station, nt.sts[station]['name'],
                       df['ts'].min().year,
                       df['ts'].max().year))

    return fig, grp
Пример #12
0
 def avgWinds(self):
     """My wind averager"""
     if not self.aSPED:
         self.sped = None
         self.drct = None
         return
     self.sped = sum(self.aSPED) / len(self.aSPED)
     utot = 0
     vtot = 0
     for i in range(len(self.aSPED)):
         u, v = uv(self.aSPED[i], self.aDrct[i])
         utot += u
         vtot += v
     uavg = utot / len(self.aSPED)
     vavg = vtot / len(self.aSPED)
     self.drct = dir(uavg, vavg)
     self.drctTxt = util.drct2text(self.drct)
Пример #13
0
def do_imagework(cameras, cid, tokens, now):
    """Process the images"""
    # Hard coded...
    drct = cameras[cid]["pan0"]
    drct_text = util.drct2text(drct)

    # Create 320x240 variant
    fn = "165.206.203.34/rwis_images/Vid-000512%s.jpg" % ("-".join(tokens), )
    try:
        i0 = Image.open(fn)
        i320 = i0.resize((320, 240), Image.ANTIALIAS)
    except Exception:
        if os.path.isfile(fn):
            os.unlink(fn)
        return

    draw = ImageDraw.Draw(i0)
    text = "(%s) %s %s" % (
        drct_text,
        cameras[cid]["name"],
        now.strftime("%-2I:%M:%S %p - %d %b %Y"),
    )
    (width, height) = FONT.getsize(text)
    draw.rectangle([5, 475 - height, 5 + width, 475], fill="#000000")
    draw.text((5, 475 - height), text, font=FONT)

    # Save 640x480
    i0.save("%s-640x480.jpg" % (cid, ))

    draw = ImageDraw.Draw(i320)
    text = "(%s) %s %s" % (
        drct_text,
        cameras[cid]["name"],
        now.strftime("%-2I:%M:%S %p - %d %b %Y"),
    )
    (width, height) = FONT.getsize(text)
    draw.rectangle([5, 235 - height, 5 + width, 235], fill="#000000")
    draw.text((5, 235 - height), text, font=FONT)

    # Save 640x480
    i320.save("%s-320x240.jpg" % (cid, ))
    del i0
    del i320
    return drct
Пример #14
0
Файл: p16.py Проект: nbackas/iem
def highcharts(fdict):
    """ Generate the highcharts variant"""
    import matplotlib
    matplotlib.use('agg')
    from windrose.windrose import histogram
    ctx = get_context(fdict)
    dir_edges, var_bins, table = histogram(ctx['df']['drct'].values,
                                           ctx['df']['smph'].values,
                                           np.array([0, 2, 5, 7, 10, 15,
                                                     20]), 18, True)
    arr = [drct2text(mydir) for mydir in dir_edges]
    return """
    var arr = """ + str(arr) + """;
    $("#ap_container").highcharts({
    series: [{name: '2 - 5',
                    data: """ + str(zip(arr, table[1])) + """,
                    _colorIndex: 0},
                   {name: '5 - 7',
                    data: """ + str(zip(arr, table[2])) + """,
                    _colorIndex: 1},
                   {name: '7 - 10',
                    data: """ + str(zip(arr, table[3])) + """,
                    _colorIndex: 2},
                   {name: '10 - 15',
                    data: """ + str(zip(arr, table[4])) + """,
                    _colorIndex: 3},
                   {name: '15 - 20',
                    data: """ + str(zip(arr, table[5])) + """,
                    _colorIndex: 4},
                   {name: '20 +',
                    data: """ + str(zip(arr, table[6])) + """,
                    _colorIndex: 5}],
    chart: {
            polar: true,
            type: 'column'
    },
    title: {
            'text': '""" + ctx['title'] + """'
    },
    subtitle: {
            'text': '""" + ctx['subtitle'] + """'
Пример #15
0
Файл: p16.py Проект: akrherz/iem
def highcharts(fdict):
    """ Generate the highcharts variant"""
    import matplotlib
    matplotlib.use('agg')
    from windrose.windrose import histogram
    ctx = get_context(fdict)
    dir_edges, var_bins, table = histogram(ctx['df']['drct'].values,
                                           ctx['df']['smph'].values,
                                           np.array([0, 2, 5, 7, 10, 15, 20]),
                                           18, True)
    arr = [drct2text(mydir) for mydir in dir_edges]
    return """
    var arr = """+str(arr)+""";
    $("#ap_container").highcharts({
    series: [{name: '2 - 5',
                    data: """+str(zip(arr, table[1]))+""",
                    _colorIndex: 0},
                   {name: '5 - 7',
                    data: """+str(zip(arr, table[2]))+""",
                    _colorIndex: 1},
                   {name: '7 - 10',
                    data: """+str(zip(arr, table[3]))+""",
                    _colorIndex: 2},
                   {name: '10 - 15',
                    data: """+str(zip(arr, table[4]))+""",
                    _colorIndex: 3},
                   {name: '15 - 20',
                    data: """+str(zip(arr, table[5]))+""",
                    _colorIndex: 4},
                   {name: '20 +',
                    data: """+str(zip(arr, table[6]))+""",
                    _colorIndex: 5}],
    chart: {
            polar: true,
            type: 'column'
    },
    title: {
            'text': '"""+ctx['title']+"""'
    },
    subtitle: {
            'text': '"""+ctx['subtitle']+"""'
Пример #16
0
 def wind_message(self):
     """Convert this into a Jabber style message"""
     drct = 0
     sknt = 0
     time = self.time.replace(tzinfo=pytz.UTC)
     if self.wind_gust:
         sknt = self.wind_gust.value("KT")
         if self.wind_dir:
             drct = self.wind_dir.value()
     if self.wind_speed_peak:
         v1 = self.wind_speed_peak.value("KT")
         d1 = self.wind_dir_peak.value()
         t1 = self.peak_wind_time.replace(tzinfo=pytz.UTC)
         if v1 > sknt:
             sknt = v1
             drct = d1
             time = t1
     key = "%s;%s;%s" % (self.station_id, sknt, time)
     if key not in WIND_ALERTS:
         WIND_ALERTS[key] = 1
         speed = datatypes.speed(sknt, 'KT')
         return ("gust of %.0f knots (%.1f mph) from %s @ %s"
                 ) % (speed.value('KT'), speed.value('MPH'),
                      drct2text(drct), time.strftime("%H%MZ"))
Пример #17
0
def get_data(ts):
    """ Get the data for this timestamp """
    iemcursor = IEM.cursor()
    cursor = ISUAG.cursor(cursor_factory=psycopg2.extras.DictCursor)
    qcdict = loadqc()
    nt = NetworkTable("ISUSM")
    data = {
        "type": "FeatureCollection",
        "crs": {
            "type": "EPSG",
            "properties": {
                "code": 4326,
                "coordinate_order": [1, 0]
            }
        },
        "features": []
    }
    # Fetch the daily values
    iemcursor.execute(
        """
    SELECT id, pday, max_tmpf, min_tmpf from summary s JOIN stations t
    on (t.iemid = s.iemid) WHERE t.network = 'ISUSM' and day = %s
    """, (ts.date(), ))
    daily = {}
    for row in iemcursor:
        daily[row[0]] = {
            'pday': row[1],
            'max_tmpf': row[2],
            'min_tmpf': row[3]
        }
    cursor.execute("""
    SELECT * from sm_hourly where valid = %s
    """, (ts, ))
    for row in cursor:
        sid = row['station']
        lon = nt.sts[sid]['lon']
        lat = nt.sts[sid]['lat']
        q = qcdict.get(sid, {})
        data['features'].append({
            "type": "Feature",
            "id": sid,
            "properties": {
                "encrh_avg":
                ("%s%%" %
                 safe(row['encrh_avg'], 1) if row['encrh_avg'] > 0 else "M"),
                "rh":
                "%s%%" % (safe(row["rh"], 0), ),
                "hrprecip": (safe_p(row['rain_mm_tot'])
                             if not q.get('precip', False) else 'M'),
                "et":
                safe_p(row['etalfalfa']),
                "bat":
                safe(row['battv_min'], 2),
                "radmj":
                safe(row['slrmj_tot'], 2),
                "tmpf":
                safe_t(row['tair_c_avg']),
                "high":
                safe_t(daily.get(sid, {}).get('max_tmpf', None), 'F'),
                "low":
                safe_t(daily.get(sid, {}).get('min_tmpf', None), 'F'),
                "pday": (safe(daily.get(sid, {}).get('pday', None), 2)
                         if not q.get('precip', False) else 'M'),
                "soil04t": (safe_t(row['tsoil_c_avg'])
                            if not q.get('soil4', False) else 'M'),
                "soil12t": (safe_t(row['t12_c_avg'])
                            if not q.get('soil12', False) else 'M'),
                "soil24t": (safe_t(row['t24_c_avg'])
                            if not q.get('soil24', False) else 'M'),
                "soil50t": (safe_t(row['t50_c_avg'])
                            if not q.get('soil50', False) else 'M'),
                "soil12m": (safe_m(row['vwc_12_avg'])
                            if not q.get('soil12', False) else 'M'),
                "soil24m": (safe_m(row['vwc_24_avg'])
                            if not q.get('soil24', False) else 'M'),
                "soil50m": (safe_m(row['vwc_50_avg'])
                            if not q.get('soil50', False) else 'M'),
                "gust":
                safe(row['ws_mph_max'], 1),
                "wind": ("%s@%.0f") %
                (drct2text(row['winddir_d1_wvt']), row['ws_mps_s_wvt'] * 2.23),
                'name':
                nt.sts[sid]['name']
            },
            "geometry": {
                "type": "Point",
                "coordinates": [lon, lat]
            }
        })
    sys.stdout.write(json.dumps(data))
Пример #18
0
def plotter(fdict):
    """ Go """
    pgconn = get_dbconn("asos")
    ctx = get_autoplot_context(fdict, get_description())

    station = ctx["zstation"]
    units = ctx["units"]
    df = read_sql(
        """
        select date_trunc('hour', valid at time zone 'UTC') as ts,
        avg(sknt) as sknt, max(drct) as drct from alldata
        WHERE station = %s and sknt is not null and drct is not null
        GROUP by ts
    """,
        pgconn,
        params=(station, ),
        index_col=None,
    )
    if df.empty:
        raise NoDataFound("No Data Found.")
    sknt = speed(df["sknt"].values, "KT")
    drct = direction(df["drct"].values, "DEG")
    df["u"], df["v"] = [x.value("MPS") for x in meteorology.uv(sknt, drct)]
    df["month"] = df["ts"].dt.month
    grp = df[["month", "u", "v", "sknt"]].groupby("month").mean()
    grp["u_%s" % (units, )] = speed(grp["u"].values, "KT").value(units.upper())
    grp["v_%s" % (units, )] = speed(grp["u"].values, "KT").value(units.upper())
    grp["sped_%s" % (units, )] = speed(grp["sknt"].values,
                                       "KT").value(units.upper())
    drct = meteorology.drct(speed(grp["u"].values, "KT"),
                            speed(grp["v"].values, "KT"))
    grp["drct"] = drct.value("DEG")
    maxval = grp["sped_%s" % (units, )].max()
    (fig, ax) = plt.subplots(1, 1)
    ax.barh(grp.index.values,
            grp["sped_%s" % (units, )].values,
            align="center")
    ax.set_xlabel("Average Wind Speed [%s]" % (UNITS[units], ))
    ax.set_yticks(grp.index.values)
    ax.set_yticklabels(calendar.month_abbr[1:])
    ax.grid(True)
    ax.set_xlim(0, maxval * 1.2)
    for mon, row in grp.iterrows():
        ax.text(
            maxval * 1.1,
            mon,
            drct2text(row["drct"]),
            ha="center",
            va="center",
            bbox=dict(color="white"),
        )
        ax.text(
            row["sped_%s" % (units, )] * 0.98,
            mon,
            "%.1f" % (row["sped_%s" % (units, )], ),
            ha="right",
            va="center",
            bbox=dict(color="white", boxstyle="square,pad=0.03"),
        )
    ax.set_ylim(12.5, 0.5)
    ax.set_title(("[%s] %s [%s-%s]\nMonthly Average Wind Speed and"
                  " Vector Average Direction") % (
                      station,
                      ctx["_nt"].sts[station]["name"],
                      df["ts"].min().year,
                      df["ts"].max().year,
                  ))

    return fig, grp
Пример #19
0
        continue

    t = tokens[0]
    cid = "IDOT-%s-%s" % (t[0], t[2])
    gmt = datetime.datetime(int(t[3]), int(t[4]), int(t[5]), int(t[6]),
                            int(t[7]))
    gmt = gmt.replace(tzinfo=pytz.timezone("UTC"))
    now = gmt.astimezone(pytz.timezone("America/Chicago"))
    if cid not in cameras:
        print "ingest_dot_webcams.py unknown CameraID: %s" % (cid, )
        cameras[cid] = {'pan0': 0, 'name': 'unknown'}

    # Hard coded...
    d = cameras[cid]
    drct = d['pan0']
    drctTxt = util.drct2text(drct)

    # Create 320x240 variant
    fn = "165.206.203.34/rwis_images/Vid-000512%s.jpg" % ("-".join(t), )
    try:
        i0 = Image.open(fn)
        i320 = i0.resize((320, 240), Image.ANTIALIAS)
    except:
        if os.path.isfile(fn):
            os.unlink(fn)
        continue

    draw = ImageDraw.Draw(i0)
    s = "(%s) %s %s" % (drctTxt, d['name'],
                        now.strftime("%-2I:%M:%S %p - %d %b %Y"))
    (w, h) = font.getsize(s)
Пример #20
0
def main():
    ''' Go!'''
    ncursor.execute("""
    DELETE from lsrs WHERE valid > %s and valid < %s
    """, (RT_T0 - datetime.timedelta(minutes=300),
          RT_T1 + datetime.timedelta(minutes=300)))
    print('Removed %s rows from nwa lsr table' % (ncursor.rowcount,))

    # Get DMX coords in 26915
    pcursor.execute("""SELECT
        ST_x( ST_transform(
        ST_GeomFromEWKT('SRID=4326;POINT(-93.723892 41.731220)'), 26915)) as x,
        ST_y( ST_transform(
        ST_GeomFromEWKT('SRID=4326;POINT(-93.723892 41.731220)'), 26915)) as y
        """)
    row = pcursor.fetchone()
    radx = row['x']
    rady = row['y']

    # Get all LSRs within 230m of the nexrad
    pcursor.execute("""SELECT *, ST_astext(geom) as tgeom,
        ST_x( ST_transform( geom, 26915) ) -
            ST_x( ST_transform(
                ST_GeomFromEWKT('SRID=4326;POINT(%s %s)'), 26915)) as offset_x,
        ST_y( ST_transform( geom, 26915) ) -
            ST_y( ST_transform(
                ST_GeomFromEWKT('SRID=4326;POINT(%s %s)'), 26915)) as offset_y
        from lsrs WHERE valid BETWEEN '%s' and '%s'
        and ST_distance( ST_transform(geom, 26915),
            ST_transform( ST_GeomFromEWKT('SRID=4326;POINT(%s %s)'), 26915))
        < (230.0 / 0.6214 * 1000.0)
    """ % (NEXRAD_LON, NEXRAD_LAT, NEXRAD_LON, NEXRAD_LAT,
           (ARCHIVE_T0 - datetime.timedelta(minutes=120)
            ).strftime("%Y-%m-%d %H:%M+00"),
           (ARCHIVE_T1 + datetime.timedelta(minutes=120)
            ).strftime("%Y-%m-%d %H:%M+00"), NEXRAD_LON, NEXRAD_LAT))

    for row in pcursor:
        locx = radx + row['offset_x']
        locy = rady + row['offset_y']
        # Locate nearest city and distance, hmm
        sql = """
            SELECT name, ST_distance(ST_transform(the_geom, 26915),
            ST_GeomFromEWKT('SRID=26915;POINT(%s %s)')) as d,
            %s - ST_x(ST_transform(the_geom,26915)) as offsetx,
            %s - ST_y(ST_transform(the_geom,26915)) as offsety
            from cities_iowa
            ORDER by d ASC LIMIT 1
        """ % (locx, locy, locx, locy)
        ncursor.execute(sql)
        row2 = ncursor.fetchone()
        deg = getdir(0 - row2['offsetx'], 0 - row2['offsety'])
        drct = util.drct2text(deg)
        miles = row2['d'] * 0.0006214  # meters -> miles
        newcity = "%.1f %s %s" % (miles, drct, row2['name'])
        city = row['city'] if REAL88D == FAKE88D else newcity

        # Compute the new valid time
        ts = row['valid']
        offset = ((ts - ARCHIVE_T0).days * 86400. +
                  (ts - ARCHIVE_T0).seconds) / SPEEDUP  # Speed up!
        valid = RT_T0 + datetime.timedelta(seconds=offset)

        # Query for WFO
        sql = """
        SELECT * from nws_ugc WHERE
   ST_transform(ST_GeomFromEWKT('SRID=26915;POINT(%s %s)'),4326) && geom
   and ST_Contains(geom,
           ST_transform(ST_GeomFromEWKT('SRID=26915;POINT(%s %s)'),4326))
        """ % (locx, locy, locx, locy)
        ncursor.execute(sql)
        row2 = ncursor.fetchone()
        wfo = row['wfo'] if FAKE88D == REAL88D else row2['wfo']
        cnty = row['county'] if FAKE88D == REAL88D else row2['name']
        st = row['state'] if FAKE88D == REAL88D else row2['state']

        # newremark = "%s\n[WAS: %s %s]" % (row['source'], row['city'],
        #                                  row['county'])
        # remark = row['remark'] if FAKE88D == REAL88D else newremark

        sql = """
        INSERT into lsrs(valid, type, magnitude, city, county, state,
        source, remark, wfo, typetext, geom) values ('%s', '%s', %s, '%s',
        '%s', '%s', '%s', '%s', '%s', '%s',
        ST_transform( ST_GeomFromEWKT('SRID=26915;POINT(%s %s)'), 4326))
        RETURNING ST_x(geom) as x, ST_y(geom) as y
        """ % (valid.strftime("%Y-%m-%d %H:%M+00"), row['type'],
               row['magnitude'] or 0, city.replace("'", ""),
               cnty.replace("'", ""), st, row['source'],
               row['remark'], wfo, row['typetext'], locx, locy)
        ncursor.execute(sql)
        row2 = ncursor.fetchone()
        print(("%s,%s,%.3f,%.3f,%s,%s,%s,%s,%s,%s"
               ) % (ts.strftime("%Y-%m-%d %H:%M"),
                    valid.strftime("%Y-%m-%d %H:%M"),
                    row2['x'], row2['y'],
                    row['magnitude'], row['typetext'],
                    row['source'], city, row['city'],
                    row['remark']))
    ncursor.close()
    NWA.commit()
    NWA.close()
Пример #21
0
def plotter(fdict):
    """ Go """
    import matplotlib
    matplotlib.use('agg')
    import matplotlib.pyplot as plt
    import matplotlib.patheffects as PathEffects
    pgconn = psycopg2.connect(database='iem', host='iemdb', user='******')
    cursor = pgconn.cursor(cursor_factory=psycopg2.extras.DictCursor)

    station = fdict.get('zstation', 'AMW')
    network = fdict.get('network', 'IA_ASOS')
    units = fdict.get('units', 'MPH').upper()
    if units not in PDICT:
        units = 'MPH'
    year = int(fdict.get('year', datetime.datetime.now().year))
    month = int(fdict.get('month', datetime.datetime.now().month))
    sts = datetime.date(year, month, 1)
    ets = (sts + datetime.timedelta(days=35)).replace(day=1)
    nt = NetworkTable(network)

    cursor.execute(
        """
      SELECT day, avg_sknt, vector_avg_drct from summary s JOIN stations t
      ON (t.iemid = s.iemid) WHERE t.id = %s and t.network = %s and
      s.day >= %s and s.day < %s ORDER by day ASC
    """, (station, network, sts, ets))
    days = []
    drct = []
    sknt = []
    for row in cursor:
        if row[1] is None:
            continue
        days.append(row[0].day)
        drct.append(row[2])
        sknt.append(row[1])
    if len(sknt) == 0:
        return "ERROR: No Data Found"
    df = pd.DataFrame(
        dict(day=pd.Series(days), drct=pd.Series(drct), sknt=pd.Series(sknt)))
    sknt = speed(np.array(sknt), 'KT').value(units)
    (fig, ax) = plt.subplots(1, 1)
    ax.bar(np.array(days) - 0.4, sknt, ec='green', fc='green')
    pos = max([min(sknt) / 2.0, 0.5])
    for d, _, r in zip(days, sknt, drct):
        draw_line(plt, d, max(sknt) + 0.5, (270. - r) / 180. * np.pi)
        txt = ax.text(d,
                      pos,
                      drct2text(r),
                      ha='center',
                      rotation=90,
                      color='white',
                      va='center')
        txt.set_path_effects(
            [PathEffects.withStroke(linewidth=2, foreground="k")])
    ax.grid(True, zorder=11)
    ax.set_title(("%s [%s]\n%s Daily Average Wind Speed and Direction") %
                 (nt.sts[station]['name'], station, sts.strftime("%b %Y")))
    ax.set_xlim(0.5, max(days) + 0.5)
    ax.set_ylim(top=max(sknt) + 2)

    ax.set_ylabel("Average Wind Speed [%s]" % (PDICT.get(units), ))

    return fig, df
Пример #22
0
def highcharts(fdict):
    """ Generate the highcharts variant"""
    ctx = get_context(fdict)
    dir_edges, _, table = histogram(
        ctx["df"]["drct"].values,
        ctx["df"]["smph"].values,
        np.array([0, 2, 5, 7, 10, 15, 20]),
        18,
        True,
    )
    arr = [drct2text(mydir) for mydir in dir_edges]
    return ("""
    var arr = """ + str(arr) + """;
    $("#ap_container").highcharts({
    series: [{name: '2 - 5',
                    data: """ + str(list(zip(arr, table[1]))) + """,
                    _colorIndex: 0},
                   {name: '5 - 7',
                    data: """ + str(list(zip(arr, table[2]))) + """,
                    _colorIndex: 1},
                   {name: '7 - 10',
                    data: """ + str(list(zip(arr, table[3]))) + """,
                    _colorIndex: 2},
                   {name: '10 - 15',
                    data: """ + str(list(zip(arr, table[4]))) + """,
                    _colorIndex: 3},
                   {name: '15 - 20',
                    data: """ + str(list(zip(arr, table[5]))) + """,
                    _colorIndex: 4},
                   {name: '20 +',
                    data: """ + str(list(zip(arr, table[6]))) + """,
                    _colorIndex: 5}],
    chart: {
            polar: true,
            type: 'column'
    },
    title: {
            'text': '""" + ctx["title"] + """'
    },
    subtitle: {
            'text': '""" + ctx["subtitle"] + """'
    },
    pane: {
            'size': '85%'
    },
    legend: {
        title: {text: 'Wind Speed [MPH]'},
            verticalAlign: 'bottom',
            layout: 'horizontal'
    },
    xAxis: {
        'tickInterval': 18./8.,
        'labels': {
                   formatter: function(){
                       var v = this.value.toFixed(1);
                       if (v == '0.0') {return 'N';}
                       if (v == '2.3') {return 'NE';}
                       if (v == '4.5') {return 'E';}
                       if (v == '6.8') {return 'SE';}
                       if (v == '9.0') {return 'S';}
                       if (v == '11.3') {return 'SW';}
                       if (v == '13.5') {return 'W';}
                       if (v == '15.8') {return 'NW';}
                       return v;
                   }
        }
    },
    yAxis: {
            'min': 0,
            'endOnTick': false,
            'showLastLabel': true,
            'title': {
                'text': 'Frequency (%)'
            },
            'reversedStacks': false
        },
    tooltip: {
        positioner: function () {
                return { x: 10, y: 10 };
            },
            'valueSuffix': '%',
            shared: true,
            valueDecimals: 1,
            formatter: function () {
            var s = '<b>' + arr[this.x] +
                    ' ('+ this.points[0].total.toFixed(1)+'%)</b>';

            $.each(this.points, function () {
                s += '<br/>' + this.series.name + ': ' +
                    this.y.toFixed(1) + '%';
            });

            return s;
        },
    },
    plotOptions: {
            'series': {
                'stacking': 'normal',
                'shadow': false,
                'groupPadding': 0,
                'pointPlacement': 'on'
            }
    }
    });
    """)
Пример #23
0
        continue

    t = tokens[0]
    cid = "IDOT-%s-%s" % (t[0], t[2])
    gmt = datetime.datetime(int(t[3]), int(t[4]), int(t[5]),
                            int(t[6]), int(t[7]))
    gmt = gmt.replace(tzinfo=pytz.timezone("UTC"))
    now = gmt.astimezone(pytz.timezone("America/Chicago"))
    if cid not in cameras:
        print "ingest_dot_webcams.py unknown CameraID: %s" % (cid,)
        cameras[cid] = {'pan0': 0, 'name': 'unknown'}

    # Hard coded...
    d = cameras[cid]
    drct = d['pan0']
    drctTxt = util.drct2text(drct)

    # Create 320x240 variant
    fn = "165.206.203.34/rwis_images/Vid-000512%s.jpg" % ("-".join(t),)
    try:
        i0 = Image.open(fn)
        i320 = i0.resize((320, 240), Image.ANTIALIAS)
    except:
        if os.path.isfile(fn):
            os.unlink(fn)
        continue

    draw = ImageDraw.Draw(i0)
    s = "(%s) %s %s" % (drctTxt, d['name'],
                        now.strftime("%-2I:%M:%S %p - %d %b %Y"))
    (w, h) = font.getsize(s)
Пример #24
0
def get_data(ts):
    """ Get the data for this timestamp """
    iemcursor = IEM.cursor()
    cursor = ISUAG.cursor(cursor_factory=psycopg2.extras.DictCursor)
    qcdict = loadqc()
    nt = NetworkTable("ISUSM", only_online=False)
    data = {"type": "FeatureCollection", "features": []}
    # Fetch the daily values
    iemcursor.execute(
        """
    SELECT id, pday, max_tmpf, min_tmpf from summary s JOIN stations t
    on (t.iemid = s.iemid) WHERE t.network = 'ISUSM' and day = %s
    """,
        (ts.date(),),
    )
    daily = {}
    for row in iemcursor:
        daily[row[0]] = {
            "pday": row[1],
            "max_tmpf": row[2],
            "min_tmpf": row[3],
        }
    cursor.execute(
        """
    SELECT h.station,
        h.encrh_avg,
        coalesce(m.rh_avg_qc, h.rh_qc) as rh,
        h.rain_mm_tot,
        etalfalfa,
        battv_min,
        coalesce(m.slrkj_tot_qc * 3600 / 1000000, h.slrmj_tot_qc) as slrmj_tot,
        coalesce(m.tair_c_avg, h.tair_c_avg) as tair_c_avg,
        coalesce(m.tsoil_c_avg_qc, h.tsoil_c_avg_qc) as tsoil_c_avg_qc,
        coalesce(m.t12_c_avg_qc, h.t12_c_avg_qc) as t12_c_avg_qc,
        coalesce(m.t24_c_avg_qc, h.t24_c_avg_qc) as t24_c_avg_qc,
        coalesce(m.t50_c_avg_qc, h.t50_c_avg_qc) as t50_c_avg_qc,
        coalesce(m.calcvwc12_avg_qc, h.calc_vwc_12_avg_qc)
            as calc_vwc_12_avg_qc,
        coalesce(m.calcvwc24_avg_qc, h.calc_vwc_24_avg_qc)
            as calc_vwc_24_avg_qc,
        coalesce(m.calcvwc50_avg_qc, h.calc_vwc_50_avg_qc)
            as calc_vwc_50_avg_qc,
        coalesce(m.ws_mph_max, h.ws_mph_max) as ws_mph_max,
        coalesce(m.winddir_d1_wvt, h.winddir_d1_wvt) as winddir_d1_wvt,
        coalesce(m.ws_mph_s_wvt * 0.447, h.ws_mps_s_wvt)as ws_mps_s_wvt
    from sm_hourly h LEFT JOIN sm_minute m on (h.station = m.station and
    h.valid = m.valid)
    where h.valid = %s
    """,
        (ts,),
    )
    for row in cursor:
        sid = row["station"]
        if sid not in nt.sts:
            continue
        lon = nt.sts[sid]["lon"]
        lat = nt.sts[sid]["lat"]
        q = qcdict.get(sid, {})
        data["features"].append(
            {
                "type": "Feature",
                "id": sid,
                "properties": {
                    "encrh_avg": (
                        "%s%%" % safe(row["encrh_avg"], 1)
                        if row["encrh_avg"] is not None
                        and row["encrh_avg"] > 0
                        else "M"
                    ),
                    "rh": "%s%%" % (safe(row["rh"], 0),),
                    "hrprecip": (
                        safe_p(row["rain_mm_tot"])
                        if not q.get("precip", False)
                        else "M"
                    ),
                    "et": safe_p(row["etalfalfa"]),
                    "bat": safe(row["battv_min"], 2),
                    "radmj": safe(row["slrmj_tot"], 2),
                    "tmpf": safe_t(row["tair_c_avg"]),
                    "high": safe_t(
                        daily.get(sid, {}).get("max_tmpf", None), "F"
                    ),
                    "low": safe_t(
                        daily.get(sid, {}).get("min_tmpf", None), "F"
                    ),
                    "pday": (
                        safe(daily.get(sid, {}).get("pday", None), 2)
                        if not q.get("precip", False)
                        else "M"
                    ),
                    "soil04t": (
                        safe_t(row["tsoil_c_avg_qc"])
                        if not q.get("soil4", False)
                        else "M"
                    ),
                    "soil12t": (
                        safe_t(row["t12_c_avg_qc"])
                        if not q.get("soil12", False)
                        else "M"
                    ),
                    "soil24t": (
                        safe_t(row["t24_c_avg_qc"])
                        if not q.get("soil24", False)
                        else "M"
                    ),
                    "soil50t": (
                        safe_t(row["t50_c_avg_qc"])
                        if not q.get("soil50", False)
                        else "M"
                    ),
                    "soil12m": (
                        safe_m(row["calc_vwc_12_avg_qc"])
                        if not q.get("soil12", False)
                        else "M"
                    ),
                    "soil24m": (
                        safe_m(row["calc_vwc_24_avg_qc"])
                        if not q.get("soil24", False)
                        else "M"
                    ),
                    "soil50m": (
                        safe_m(row["calc_vwc_50_avg_qc"])
                        if not q.get("soil50", False)
                        else "M"
                    ),
                    "gust": safe(row["ws_mph_max"], 1),
                    "wind": ("%s@%.0f")
                    % (
                        drct2text(row["winddir_d1_wvt"]),
                        row["ws_mps_s_wvt"] * 2.23,
                    ),
                    "name": nt.sts[sid]["name"],
                },
                "geometry": {"type": "Point", "coordinates": [lon, lat]},
            }
        )
    return json.dumps(data)