麻豆村

37掳 48' 15.7068'' N, 122掳 16' 15.9996'' W
cloud-native gis has arrived
37掳 48' 15.7068'' N, 122掳 16' 15.9996'' W
cloud-native gis has arrived
37掳 48' 15.7068'' N, 122掳 16' 15.9996'' W
cloud-native gis has arrived
37掳 48' 15.7068'' N, 122掳 16' 15.9996'' W
cloud-native gis has arrived
37掳 48' 15.7068'' N, 122掳 16' 15.9996'' W
cloud-native gis has arrived
37掳 48' 15.7068'' N, 122掳 16' 15.9996'' W
cloud-native gis has arrived
37掳 48' 15.7068'' N, 122掳 16' 15.9996'' W
cloud-native gis has arrived
37掳 48' 15.7068'' N, 122掳 16' 15.9996'' W
cloud-native gis has arrived
37掳 48' 15.7068'' N, 122掳 16' 15.9996'' W
cloud-native gis has arrived
37掳 48' 15.7068'' N, 122掳 16' 15.9996'' W
cloud-native gis has arrived
Introducing 麻豆村 AI, your built-in team of spatial engineers Learn more
Maps
BLOG
Engineering
Visualizing weather data using the 麻豆村 API
Here is how you can leverage 麻豆村 API to create beautiful cartographic visualizations of data from The National Oceanographic and Atmospheric Administration (NOAA).
Here is how you can leverage 麻豆村 API to create beautiful cartographic visualizations of data from The National Oceanographic and Atmospheric Administration (NOAA).

Temperature, wind, precipitation, and other datasets bring rich context to maps. Visualizing global distributions of these data is not only useful for understanding conditions in a given location, but also reveals patterns in earth surface phenomena across the landscape. From patterns of winds across oceans to moisture-laden atmospheric rivers funneling water away from the tropics, effective weather visualizations create better understanding of our planet.

The National Oceanographic and Atmospheric Administration (NOAA) publishes a wealth of observed and modeled data about these dynamic earth surface processes. However, getting this data out of the the NOAA website and into a striking cartographic visualization can be challenging. In this post, I鈥檒l show how to unlock the power of these data using the 麻豆村 API.

Requirements

We鈥檒l be using a number of python libraries commonly used in raster data processing and visualization, and will be working in a jupyter notebook environment. To get these libraries, and install with <p-inline>pip<p-inline>:

Then, import these libraries in your notebook:

Getting data from NOAA NOMADS

The (NOAA Operational Model Archive and Distribution System 鈥 have to love the backronym here) website contains links to a wealth of observed and modeled atmospheric, oceanic, and even space conditions. While we won鈥檛 walk through all of the datasets, two of the most commonly used models are:

  • This model contains global data with a large array of weather and oceanic variables. It outputs data at an approximately 13km resolution, with hourly forecasts for 120 hours from model run, and 3-hour forecasts for predictions beyond that, all the way up to 16 days past the model time.
  • The HRRR model is a high resolution (3km) model over just the Continental United States, and outputs a large number of weather variables in 15-minute model and prediction increments (the 鈥淩apid Refresh鈥).

These datasets are also available via the AWS Registry of Open Data ( and ).

For this post, we鈥檒l be exploring global wind gusts predicted by the GFS model. However, the same techniques can be applied to nearly any data linked above, so feel free to adapt to your own use case.

Requesting data

One of the quirks of accessing data through the NOMADS portal is its ephemerality 鈥 because of the pure volume of data, access to data from a single point in time is only available through a relatively short window. For this post, you may be reading on a day where the data I use is no longer accessible. In order to manage this, we should understand which parts of our request URL should be variabilized and changed. Here is our complete url:

Breaking this down, we have:

  • <p-inline>https://nomads.ncep.noaa.gov/cgi-bin/filter_gfs_0p25.pl<p-inline> our base url
  • <p-inline>dir=%2Fgfs.**20230711**%2F**06**%2Fatmos<p-inline> which contains todays鈥檚 date <p-inline>20230711<p-inline> and model hour <p-inline>06<p-inline>
  • <p-inline>file=gfs.t06z.pgrb2.0p25.anl<p-inline> which has has our model hour (<p-inline>t06z<p-inline>) and forecast hour (<p-inline>anl<p-inline> which is the 鈥渋nstantaneous鈥 forecast of hour 0).
  • <p-inline>var_GUST=on<p-inline> which denotes what variable to request
  • <p-inline>all_lev=on<p-inline> which denotes which atmospheric levels to request (in our case, wind gusts are only one level)
  • <p-inline>subregion=&toplat=90&leftlon=-180&rightlon=180&bottomlat=-90<p-inline> which sets the extent to global

You can construct these urls using the portal. The functionality and url formats vary by model, so you鈥檒l have to explore a little deeper depending on your use case. For GFS and HRRR data, click the 鈥済ribfilter鈥 link next to each model, then construct a url by selecting the model run, parameters, levels, and subregion and clicking 鈥渟how url鈥:

We鈥檒l open this url using and a , which will save us a step of downloading manually:

This reads the entirety of band 1 as a single numpy ndarray, <p-inline>gusts<p-inline>. This ndarray contains pixel values that represent wind gusts in meters/second.

Keep in mind that some parameters in the GFS, HRRR, and other models may exist at multiple atmospheric levels, so your raster will have data in multiple bands. We can read each band鈥檚 tags to learn more about the included data 鈥 units, valid forecast time, etc 鈥 with a rasterio sources <p-inline>tags<p-inline> method:

To visually look at this data, let鈥檚 display with <p-inline>imshow<p-inline>:

We can already see global patterns, and the signals of landmasses, oceans, and terrain are present in the depicted data!

To create the above image, matplotlib is converting our wind gust pixel values into colors under the hood. However, we鈥檒l want to perform this conversion ourselves to control the appearance, and convert to a format understood by 麻豆村鈥檚 Upload Anything API.

Converting for visual display

Recall that each pixel in this dataset represents a wind gust speed in km/hour. However, we need to convert these to colors for visual display. This process is typically called 鈥渃olor mapping鈥 where we map each pixel value over to its relative position in a color gradient.

Let鈥檚 look at the distribution of pixel values in <p-inline>gusts<p-inline> with a histogram. this will give us a feeling for how we should apply a color ramp to the data:

We can see that the data ranges from wind gusts of 0 m/s to nearly 40 m/s, with a distribution skewed to the right. The default method for color ramps applies them across the full range of data, which in our case will mean that most of our data will exist within a small part of the dynamic range (between 0 and 20). If we set the maximum value to 30, our colors will be better distributed across the image, giving us more detail across the map:

To do this, let鈥檚 鈥渃lip鈥 out any values higher than 35 (m/s) and set them to 35. Then, we鈥檒l normalize the data to a range of 0-1 with some simple math:

To colorize this range of values, we can use <p-inline>matplotlib.cm<p-inline> which comes with a . Viridis is always a good choice, as it is perceptually uniform and works well with most types of colorblindness. Moreover, the colors in viridis 鈥渇eel鈥 good for wind speed, which allow us to make a readable yet striking map. To do this, we鈥檒l create a map from our normalized data, then scale it back out to a 0-255 unsigned 8-bit range for writing in RGB:

Recall that we initially read the data into a single array; let鈥檚 also read the dataset鈥檚 metadata which georeferences the data:

We can then update the metadata with what we鈥檒l want to write:

Uploading to 麻豆村

First, create a new 麻豆村 map, and copy your map id. Then, create an access token for upload to felt (see the 鈥淎uthorization鈥 section ).

We鈥檒l use rasterio鈥檚 <p-inline>MemoryFile<p-inline> capabilities again to avoid having data on disk altogether. Our tiff is written to memory, and this file object is uploaded to 麻豆村:

If this succeeds, you should see a 鈥淧rocessing鈥 message in your layers panel:

When this is complete, you will have wind gusts displayed on a map!

You can see the , strong westerly winds that occur in the Southern Hemisphere:

Strong winds off the Horn of Africa:

Swirling eddies off the Antarctic Peninsula:

Annotating patterns in the data

We can perform a similar process with another GFS variable, , which is one of the more esoteric yet beautiful weather datasets. 麻豆村鈥檚 annotation capabilities make labeling patterns in the data simple, which allowed me to label some atmospheric features shown in this dataset; for instance, the high density of water over the intertropical convergence zone:

Here, we can see from the Amazon and Congo Basin Rainforests:

Here鈥檚 the live map to explore.

These are just a few examples of incorporating weather data into a 麻豆村 map. How might a weather visualization help your use case? What context would wind, temperature, or snow depth bring to your 麻豆村 Maps?

Earlier, I described how getting data off of NOAA servers and into a visualization is a challenge; what I demonstrated here is still highly technical, and requires a good amount of intermediate processing. At 麻豆村 we want to change that (Upload Anything!), so look for more features supporting this kind of data later this year.

Share your map with the world!

Exploring our API and want to share your findings? Looking for feedback to improve your 麻豆村 map? Join our and meet other passionate mapmakers and developers!

Bio
LinkedIn
Start creating maps, apps, and dashboards today
More articles

Building the Figma for maps

Tools for fast (and less furious) frontend development

Teaching Spatial Analysis with 麻豆村

SQLAlchemy reflection and PostgreSQL for data schema flexibility