Fetching temperature data and displaying it on a map

This example demonstrates using MesoPy to download data from the MesoWest data repository to display on a map.

External libraries used:

  • MesoPy
  • matplotlib
  • cartopy

Example

First we import the Meso object from MesoPy to make our data request, pass in an API token, and request air temperature values in the last 90 minutes at the stations below.


In [4]:
from MesoPy import Meso

# Pass in a token to the meso object to validate against the API. Please use your own (see README for details)!
m = Meso(token='demotoken')

# Use to lookup stations, could specify counties or whatever here
# findstationids = m.station_list(state='CO')
# print(findstationids)

# Grab most recent temp (F) ob in last 90 min at each of the below stations
stations = ['kgxy, kccu, kcos, kden, kgjt, kbdu, kpub, klhx, kspd, kdro, ksbs, keeo, kguc, klic, '
            'kstk, kals, ktad']
latest = m.latest(stid=stations, within='90', vars='air_temp', units='temp|F')

# Print latest to the notebook
latest


Out[4]:
{u'STATION': [{u'ELEVATION': u'7536',
   u'ID': u'89',
   u'LATITUDE': u'37.43933',
   u'LONGITUDE': u'-105.86180',
   u'MNET_ID': u'1',
   u'NAME': u'San Luis Valley Regional Airport',
   u'OBSERVATIONS': {u'air_temp_value_1': {u'date_time': u'2015-07-09T16:52:00Z',
     u'value': u'64.94'}},
   u'SENSOR_VARIABLES': {u'air_temp': {u'air_temp_value_1': {u'position': u'2'}}},
   u'STATE': u'CO',
   u'STATUS': u'ACTIVE',
   u'STID': u'KALS',
   u'TIMEZONE': u'US/Mountain'},
  {u'ELEVATION': u'6181',
   u'ID': u'140',
   u'LATITUDE': u'38.80948',
   u'LONGITUDE': u'-104.68872',
   u'MNET_ID': u'1',
   u'NAME': u'City Of Colorado Springs Municipal Airport',
   u'OBSERVATIONS': {u'air_temp_value_1': {u'date_time': u'2015-07-09T16:54:00Z',
     u'value': u'64.04'}},
   u'SENSOR_VARIABLES': {u'air_temp': {u'air_temp_value_1': {u'position': u'2'}}},
   u'STATE': u'CO',
   u'STATUS': u'ACTIVE',
   u'STID': u'KCOS',
   u'TIMEZONE': u'US/Mountain'},
  {u'ELEVATION': u'5404',
   u'ID': u'153',
   u'LATITUDE': u'39.84658',
   u'LONGITUDE': u'-104.65622',
   u'MNET_ID': u'1',
   u'NAME': u'Denver, Denver International Airport',
   u'OBSERVATIONS': {u'air_temp_value_1': {u'date_time': u'2015-07-09T16:53:00Z',
     u'value': u'71.06'}},
   u'SENSOR_VARIABLES': {u'air_temp': {u'air_temp_value_1': {u'position': u'2'}}},
   u'STATE': u'CO',
   u'STATUS': u'ACTIVE',
   u'STID': u'KDEN',
   u'TIMEZONE': u'US/Mountain'},
  {u'ELEVATION': u'6627',
   u'ID': u'163',
   u'LATITUDE': u'37.14312',
   u'LONGITUDE': u'-107.76023',
   u'MNET_ID': u'1',
   u'NAME': u'Durango-La Plata County Airport',
   u'OBSERVATIONS': {u'air_temp_value_1': {u'date_time': u'2015-07-09T16:53:00Z',
     u'value': u'68.0'}},
   u'SENSOR_VARIABLES': {u'air_temp': {u'air_temp_value_1': {u'position': u'2'}}},
   u'STATE': u'CO',
   u'STATUS': u'ACTIVE',
   u'STID': u'KDRO',
   u'TIMEZONE': u'US/Mountain'},
  {u'ELEVATION': u'6365',
   u'ID': u'171',
   u'LATITUDE': u'40.04440',
   u'LONGITUDE': u'-107.88832',
   u'MNET_ID': u'1',
   u'NAME': u'Meeker Airport',
   u'OBSERVATIONS': {u'air_temp_value_1': {u'date_time': u'2015-07-09T16:53:00Z',
     u'value': u'69.08'}},
   u'SENSOR_VARIABLES': {u'air_temp': {u'air_temp_value_1': {u'position': u'2'}}},
   u'STATE': u'CO',
   u'STATUS': u'ACTIVE',
   u'STID': u'KEEO',
   u'TIMEZONE': u'US/Mountain'},
  {u'ELEVATION': u'4859',
   u'ID': u'202',
   u'LATITUDE': u'39.13389',
   u'LONGITUDE': u'-108.53861',
   u'MNET_ID': u'1',
   u'NAME': u'Grand Junction Regional Airport',
   u'OBSERVATIONS': {u'air_temp_value_1': {u'date_time': u'2015-07-09T16:53:00Z',
     u'value': u'69.98'}},
   u'SENSOR_VARIABLES': {u'air_temp': {u'air_temp_value_1': {u'position': u'2'}}},
   u'STATE': u'CO',
   u'STATUS': u'ACTIVE',
   u'STID': u'KGJT',
   u'TIMEZONE': u'US/Mountain'},
  {u'ELEVATION': u'7677',
   u'ID': u'206',
   u'LATITUDE': u'38.53333',
   u'LONGITUDE': u'-106.93333',
   u'MNET_ID': u'1',
   u'NAME': u'Gunnison, Gunnison-Crested Butte Regional Airport',
   u'OBSERVATIONS': {u'air_temp_value_1': {u'date_time': u'2015-07-09T16:55:00Z',
     u'value': u'62.6'}},
   u'SENSOR_VARIABLES': {u'air_temp': {u'air_temp_value_1': {u'position': u'2'}}},
   u'STATE': u'CO',
   u'STATUS': u'ACTIVE',
   u'STID': u'KGUC',
   u'TIMEZONE': u'US/Mountain'},
  {u'ELEVATION': u'4698',
   u'ID': u'208',
   u'LATITUDE': u'40.43333',
   u'LONGITUDE': u'-104.63333',
   u'MNET_ID': u'1',
   u'NAME': u'Greeley, Greeley-Weld County Airport',
   u'OBSERVATIONS': {u'air_temp_value_1': {u'date_time': u'2015-07-09T16:55:00Z',
     u'value': u'64.4'}},
   u'SENSOR_VARIABLES': {u'air_temp': {u'air_temp_value_1': {u'position': u'2'}}},
   u'STATE': u'CO',
   u'STATUS': u'ACTIVE',
   u'STID': u'KGXY',
   u'TIMEZONE': u'US/Mountain'},
  {u'ELEVATION': u'4193',
   u'ID': u'243',
   u'LATITUDE': u'38.04949',
   u'LONGITUDE': u'-103.51334',
   u'MNET_ID': u'1',
   u'NAME': u'La Junta Municipal Airport',
   u'OBSERVATIONS': {u'air_temp_value_1': {u'date_time': u'2015-07-09T16:53:00Z',
     u'value': u'69.98'}},
   u'SENSOR_VARIABLES': {u'air_temp': {u'air_temp_value_1': {u'position': u'2'}}},
   u'STATE': u'CO',
   u'STATUS': u'ACTIVE',
   u'STID': u'KLHX',
   u'TIMEZONE': u'US/Mountain'},
  {u'ELEVATION': u'5358',
   u'ID': u'244',
   u'LATITUDE': u'39.27334',
   u'LONGITUDE': u'-103.66738',
   u'MNET_ID': u'1',
   u'NAME': u'Limon Municipal Airport',
   u'OBSERVATIONS': {u'air_temp_value_1': {u'date_time': u'2015-07-09T16:55:00Z',
     u'value': u'64.94'}},
   u'SENSOR_VARIABLES': {u'air_temp': {u'air_temp_value_1': {u'position': u'2'}}},
   u'STATE': u'CO',
   u'STATUS': u'ACTIVE',
   u'STID': u'KLIC',
   u'TIMEZONE': u'US/Mountain'},
  {u'ELEVATION': u'4724',
   u'ID': u'337',
   u'LATITUDE': u'38.29000',
   u'LONGITUDE': u'-104.49833',
   u'MNET_ID': u'1',
   u'NAME': u'Pueblo, Pueblo Memorial Airport',
   u'OBSERVATIONS': {u'air_temp_value_1': {u'date_time': u'2015-07-09T16:53:00Z',
     u'value': u'71.96'}},
   u'SENSOR_VARIABLES': {u'air_temp': {u'air_temp_value_1': {u'position': u'2'}}},
   u'STATE': u'CO',
   u'STATUS': u'ACTIVE',
   u'STID': u'KPUB',
   u'TIMEZONE': u'US/Mountain'},
  {u'ELEVATION': u'6890',
   u'ID': u'370',
   u'LATITUDE': u'40.51530',
   u'LONGITUDE': u'-106.86767',
   u'MNET_ID': u'1',
   u'NAME': u'Steamboat Springs',
   u'OBSERVATIONS': {u'air_temp_value_1': {u'date_time': u'2015-07-09T16:55:00Z',
     u'value': u'64.4'}},
   u'SENSOR_VARIABLES': {u'air_temp': {u'air_temp_value_1': {u'position': u'2'}}},
   u'STATE': u'CO',
   u'STATUS': u'ACTIVE',
   u'STID': u'KSBS',
   u'TIMEZONE': u'US/Mountain'},
  {u'ELEVATION': u'4380',
   u'ID': u'400',
   u'LATITUDE': u'37.28333',
   u'LONGITUDE': u'-102.61667',
   u'MNET_ID': u'1',
   u'NAME': u'Springfield, Comanche National Grassland',
   u'OBSERVATIONS': {u'air_temp_value_1': {u'date_time': u'2015-07-09T16:56:00Z',
     u'value': u'62.96'}},
   u'SENSOR_VARIABLES': {u'air_temp': {u'air_temp_value_1': {u'position': u'2'}}},
   u'STATE': u'CO',
   u'STATUS': u'ACTIVE',
   u'STID': u'KSPD',
   u'TIMEZONE': u'US/Mountain'},
  {u'ELEVATION': u'5738',
   u'ID': u'410',
   u'LATITUDE': u'37.26218',
   u'LONGITUDE': u'-104.33184',
   u'MNET_ID': u'1',
   u'NAME': u'Perry Stokes Airport',
   u'OBSERVATIONS': {u'air_temp_value_1': {u'date_time': u'2015-07-09T15:54:00Z',
     u'value': u'66.92'}},
   u'SENSOR_VARIABLES': {u'air_temp': {u'air_temp_value_1': {u'position': u'2'}}},
   u'STATE': u'CO',
   u'STATUS': u'ACTIVE',
   u'STID': u'KTAD',
   u'TIMEZONE': u'US/Mountain'},
  {u'ELEVATION': u'12028',
   u'ID': u'8656',
   u'LATITUDE': u'39.47523',
   u'LONGITUDE': u'-106.15228',
   u'MNET_ID': u'1',
   u'NAME': u'Copper Mountain - Red Cliff Pass',
   u'OBSERVATIONS': {u'air_temp_value_1': {u'date_time': u'2015-07-09T16:35:00Z',
     u'value': u'48.2'}},
   u'SENSOR_VARIABLES': {u'air_temp': {u'air_temp_value_1': {u'position': u'2'}}},
   u'STATE': u'CO',
   u'STATUS': u'ACTIVE',
   u'STID': u'KCCU',
   u'TIMEZONE': u'US/Mountain'},
  {u'ELEVATION': u'5288',
   u'ID': u'31841',
   u'LATITUDE': u'40.0394297',
   u'LONGITUDE': u'-105.2258217',
   u'MNET_ID': u'1',
   u'NAME': u'Boulder Municipal Airport',
   u'OBSERVATIONS': {u'air_temp_value_1': {u'date_time': u'2015-07-09T16:39:00Z',
     u'value': u'64.4'}},
   u'SENSOR_VARIABLES': {u'air_temp': {u'air_temp_value_1': {u'position': u'2'}}},
   u'STATE': u'CO',
   u'STATUS': u'ACTIVE',
   u'STID': u'KBDU',
   u'TIMEZONE': u'US/Mountain'},
  {u'ELEVATION': u'4026',
   u'ID': u'32612',
   u'LATITUDE': u'40.61331',
   u'LONGITUDE': u'-103.26109',
   u'MNET_ID': u'1',
   u'NAME': u'Sterling Municipal Airport',
   u'OBSERVATIONS': {u'air_temp_value_1': {u'date_time': u'2015-07-09T16:25:00Z',
     u'value': u'64.4'}},
   u'SENSOR_VARIABLES': {u'air_temp': {u'air_temp_value_1': {u'position': u'2'}}},
   u'STATE': u'CO',
   u'STATUS': u'ACTIVE',
   u'STID': u'KSTK',
   u'TIMEZONE': u'US/Mountain'}],
 u'SUMMARY': {u'DATA_PARSING_TIME': u'2.20394134521 ms',
  u'DATA_QUERY_TIME': u'0.300077915192 ms',
  u'METADATA_RESPONSE_TIME': u'307.493925095 ms',
  u'MINUTES_SINCE_MOST_RECENT_OBSERVATION': 0.39588464895884196,
  u'MOST_RECENT_OBSERVATION': u'2015-07-09T16:58Z',
  u'NUMBER_OF_OBJECTS': 17,
  u'RESPONSE_CODE': 1,
  u'RESPONSE_MESSAGE': u'OK',
  u'TOTAL_DATA_TIME': u'302.289962769 ms',
  u'TOTAL_TIME': u'1082.44800568 ms'},
 u'UNITS': {u'air_temp': u'Fahrenheit'}}

As you can see from the above, this returns a lot of metadata as well as the temperature data we requested. Now we can pull out only the data we want and create a nice list to store everything for iterating over later.


In [5]:
data = []
[data.append((float(ob['LATITUDE']), float(ob['LONGITUDE']), float(ob['OBSERVATIONS']['air_temp_value_1']['value']),
          ob['STID'])) for ob in latest['STATION']]

# Print the data list to the notebook
data


Out[5]:
[(37.43933, -105.8618, 64.94, u'KALS'),
 (38.80948, -104.68872, 64.04, u'KCOS'),
 (39.84658, -104.65622, 71.06, u'KDEN'),
 (37.14312, -107.76023, 68.0, u'KDRO'),
 (40.0444, -107.88832, 69.08, u'KEEO'),
 (39.13389, -108.53861, 69.98, u'KGJT'),
 (38.53333, -106.93333, 62.6, u'KGUC'),
 (40.43333, -104.63333, 64.4, u'KGXY'),
 (38.04949, -103.51334, 69.98, u'KLHX'),
 (39.27334, -103.66738, 64.94, u'KLIC'),
 (38.29, -104.49833, 71.96, u'KPUB'),
 (40.5153, -106.86767, 64.4, u'KSBS'),
 (37.28333, -102.61667, 62.96, u'KSPD'),
 (37.26218, -104.33184, 66.92, u'KTAD'),
 (39.47523, -106.15228, 48.2, u'KCCU'),
 (40.0394297, -105.2258217, 64.4, u'KBDU'),
 (40.61331, -103.26109, 64.4, u'KSTK')]

Now we can display this information on our map! I wanted a terrain map to show why temperature values would be so dramatically different in Colorado so I am choosing to pull map tiles from the Mapquest server. We set up our figure, draw the points for the stations, apply a transformation so that the text displays next to the lat/long points, and then plot the text.


In [11]:
import matplotlib.pyplot as plt
from matplotlib.transforms import offset_copy
import cartopy.crs as ccrs
import cartopy.io.img_tiles as cimgt
%matplotlib inline

fig = plt.figure(figsize=(15, 12))
# Create a MapQuest open aerial instance.
map_quest_aerial = cimgt.MapQuestOpenAerial()
# Create a GeoAxes in the tile's projection.
ax = plt.axes(projection=map_quest_aerial.crs)
# Limit the extent of the map to Colorado's borders
ax.set_extent([-102.03, -109.03, 37, 41])
# Add the MapQuest data at zoom level 8.
ax.add_image(map_quest_aerial, 8)

# Plot lat/long pts with below params
for lat, lon, temp, stid in data:
    plt.plot(lon, lat, marker='o', color='y', markersize=1,
             alpha=0.7, transform=ccrs.Geodetic())
    
# Transforms for the text func we're about to call
geodetic_transform = ccrs.Geodetic()._as_mpl_transform(ax)
text_transform = offset_copy(geodetic_transform, units='dots', x=0, y=0)

# Plot temp and station id for each of the markers
for lat, lon, temp, stid in data:
    plt.text(lon, lat, stid + '\n' + str(round(temp, 1)) + u' \N{DEGREE SIGN}' + 'F',
             verticalalignment='center', horizontalalignment='center',
             transform=text_transform, fontsize=14,
             bbox=dict(facecolor='wheat', alpha=0.5, boxstyle='round'))
plt.title('Current Weather Around Colorado', fontsize=20)


Out[11]:
<matplotlib.text.Text at 0x109139b90>

In [ ]: