Publication-Quality Plots Using MATLAB and METAR Data From Nearby Airports

This tutorial will focus on producing publication-quality plots by utilizing MATLAB and the Meteorological Aerodrome Report (METAR). METAR stations record atmospheric conditions of nearby airports to ensure that aviators operate under safe conditions in the proximal airspace. Meteorologists also use METAR data for forecasting and calibration of remote sensing instruments. The procedure outlined below will use your zip code to find a nearby METAR station, parse the data from the National Weather Service, and ultimately produce a plot of recent weather data published by that METAR station. By the end of this tutorial, the user will have a better grasp of MATLAB's figure and plot tools and produce publication-quality results.


Finding Your METAR Station

First and foremost, a METAR station needs to be identified. The METAR station can be random, a local station, or a chosen location. A 4-letter identifier has to be selected in order for the MATLAB routine to request data. 

 

Steps for Finding METAR Station

  1. Visit http://www.weather.gov/
  2. Enter a Zip Code in the red box shown in Figure 1, then click 'Go'
  3. After clicking 'Go' a new page should appear showing the current conditions of the METAR station local to that Zip Code. Find and record the 4-letter identifier (see Figure 2's red box), and continue with this tutorial
  Figure 1:  Find a METAR station at weather.gov by inputting a zip code into the red box highlighted above.

Figure 1: Find a METAR station at weather.gov by inputting a zip code into the red box highlighted above.

 
  Figure 2:  The Zip Code '10024' has outputted the station for Central Park, NY. The station identifier is 'KNYC,' which I have highlighted in the red box. We will be using this identifier in MATLAB to pull the atmospheric conditions of the METAR station and plot different variables.

Figure 2: The Zip Code '10024' has outputted the station for Central Park, NY. The station identifier is 'KNYC,' which I have highlighted in the red box. We will be using this identifier in MATLAB to pull the atmospheric conditions of the METAR station and plot different variables.

Now that a station is identified, it's time to explore the METAR data. For every METAR station there is a government database where the data is stored. The data can be accessed the following way:

http://w1.weather.gov/data/obhistory/ + 'your station ID' + .html

In my case, my station ID is 'KNYC' so my full path would be:

And if the link above is clicked, the METAR database can be observed for Central Park, NY (KNYC) for the past 72 hours. Figure 3 shows the METAR station data for KNYC for a few hours on February 14, 2018. If the station selected does not show the same table, then another station should be chosen.

KNYC_METAR_Feb_13_2018.png

Figure 3:
KNYC METAR STATION
DATA


Reading and Parsing METAR Data With MATLAB

Now that a station ID has been identified, it is time to tell MATLAB to read the data from the URL created above. In the code below, MATLAB is creating a URL for the METAR station, and subsequently reading the webpage using 'webread.' The variable 'web_str_crawl' now contains the data from the METAR station for the last 72 hours.

%% URL read for Real-time Weather Station Measurements
%
clear all
close all
clc
%
station_repos = 'http://w1.weather.gov/data/obhistory/'; % This is where 
% the weather stations are located at www.weather.gov 
% You type in your address and then look for the 4-letter station
% identifier (for example, if you type in '10024' for NYC, the station
% ID for that Zip Code is 'KNYC' - Central Park's weather station
%
station_ID = 'KNYC';
%
% concatenate station repository with your local station
%
web_url = strcat(station_repos,station_ID,'.html');
%
% Crawl the URL for data
web_str_crawl = webread(web_url);

From here, I will demonstrate how to parse that data, which is currently formatted in html. The code below is a function for taking METAR station data and parsing out the variables we want and formatting them into a nice table in MATLAB. All the function needs is the URL for the METAR station. 

function station_table = data_reformat(web_url_str)
%
% Parse each row, which in html is identified by <td></td>, then put each
% into the station_parse variable
station_parse = {};
for jj = 1:length(web_url_str)-4
    if web_url_str(jj:jj+4)=='</td>'
        for kk = 1:80
            if web_url_str(jj-kk)=='>'
                station_parse{end+1} = web_url_str(jj-kk+1:jj-1);
                break
            end
        end
    end
end
% shift data to get rid of the extra headers
station_vec = {};
n_shift = 9;
for ll = 1:length(station_parse)
    if ll>=n_shift
        station_vec{ll-n_shift+1} =station_parse{ll};         
    end
end
%
% reformat the data into an easily-readable table
%
col_size = 18;
station_table = {zeros(floor(length(station_vec)/col_size)+1,col_size)};
% I rewrote the headers to make them more friendly for labeling
station_header = {'Date','Time (Local)','Wind [MPH]','Visibility [Miles]','Weather Cond.',...
    'Sky Cond.','Air Temperature [F]','Dew Point [F]','Temp (6 Hour Max) [F]','Temp (6 Hour Min)','Relative Humidity [%]',...
    'Wind Chill [F]','Heat Index [F]','Pressure (Alt, inches)','Pressure (Sea, mbar)',...
    'Precipitation (1 hr) [inches]','Precipitation (inches) [3 hr]','Precipitation (inches) [6 hr]'};
% This is the loop where the table is created and formatted
for mm = 1:floor(length(station_vec)/col_size)+1
    for nn = 1:col_size
        if mm == 1
            station_table{mm,nn} = station_header{nn};
        else
            pp = (mm-2)*col_size+nn;
            if nn==11
                station_table{mm,nn} = station_vec{pp}(1:2);
            else
                station_table{mm,nn} = station_vec{pp};
            end
        end
    end
end
  Figure 4:&nbsp; MATLAB table showing the METAR data for station 'KNYC.' The table shows all of the data for the last 72 hours for the station. It mimics the table shown in the html link above. From here, I will demonstrate how to plot the data and update the plot with a push button.

Figure 4: MATLAB table showing the METAR data for station 'KNYC.' The table shows all of the data for the last 72 hours for the station. It mimics the table shown in the html link above. From here, I will demonstrate how to plot the data and update the plot with a push button.


MATLAB Figure Formatting

In science and industry alike, visualization plays an important role in research and analysis. MATLAB also requires extensive alteration of its default plotting tools to produce beautiful visualizations. Therefore, I have included a MATLAB function that produces a figure which mimics a popular graphics tool used in the R programming language, called ggplot. With this new template, any dull MATLAB figure can be transformed into a contrasting, visually compelling visualization.

function [fig1,ax1,dcm_obj] = fig_open()
set(0,'defaultfigurecolor',[1 1 1]) % white background
set(0,'defaultaxesfontname','cambria math') % beautify the axes a bit
scrn_size = get(0,'ScreenSize'); % get size of screen
shrink_pct = 0.1; % shrink the figure by 10%
%
fig1 = figure('Visible','off','DefaultAxesFontSize',20,'Position',...
    [scrn_size(1)+(scrn_size(3)*shrink_pct) scrn_size(2)+(scrn_size(4)*shrink_pct)...
    scrn_size(3)-(scrn_size(3)*2*shrink_pct) scrn_size(4)-(scrn_size(4)*2*shrink_pct)]); % shrinking the figure
%
ax1 = gca;
dcm_obj = datacursormode(fig1); % enable control of datatips
% set(dcm_obj,'UpdateFcn',@myupdatefcn) % this will be used to configure
% data tips
set(ax1,'fontsize',22,'Color',[0.8 0.8 0.8],'gridcolor',[1 1 1],'gridalpha',0.9) % set the axis color
% to compliment the white background
%
xlabel('Time [Day HH:MM]') % x-axis date label
hold all
grid on % grid for more contrast in the axes
end
  Figure 5:&nbsp; MATLAB figure template for plotting publication-quality figures. The font size is visible, the axes are bold, the grid lines are visible and there is a nice black, white, and gray contrast in the figure.&nbsp;

Figure 5: MATLAB figure template for plotting publication-quality figures. The font size is visible, the axes are bold, the grid lines are visible and there is a nice black, white, and gray contrast in the figure. 


Plotting The METAR Variables

The final and most important part of this tutorial is the data visualization. Now that we have found our METAR station, parsed the data from weather.gov, and created our figure, we now need to plot the data. The station data above exists in a cell of strings within MATLAB. One major complication with the data is the fact that the time spans two columns: 'Date' and 'Time (Local).' Therefore, the first thing we need to do is convert those times to a single datenumber in order to plot the variables in the correct time sequence. The function for this is below:

function [datenum_vec,var_pick,var_name] = parse_and_format(station_table,var_col)
curr_time = datetime(); % get the current time for year information
datenum_vec = []; var_pick = [];
var_name = station_table{1,var_col}; % variable name
for ss = 2:length(station_table) % starting at the second row since the first contains the variable names
    var_pick = [var_pick,str2double(station_table{ss,var_col})];    
    if isempty(str2double(station_table{ss,var_col})) % if there is no data in the row, skip the loop
        continue
    end
    if ss==2 % making sure the first row is the reference year, month, and day
        datenum_vec = [datenum_vec,datenum(year(curr_time),month(curr_time),str2num(station_table{ss}),str2num(station_table{ss,2}(1:2)),str2num(station_table{ss,2}(4:5)),0)];
    else
        if str2double(station_table{ss})>str2double(station_table{2}) % this ensures that if the date grows, then the day is in the previous month
            if month(curr_time)==1 % if it's december, then we have to roll back the month and year
                datenum_vec = [datenum_vec,datenum(year(curr_time)-1,12,str2double(station_table{ss}),str2double(station_table{ss,2}(1:2)),str2double(station_table{ss,2}(4:5)),0)];                            
            else % end of month, roll back month
                datenum_vec = [datenum_vec,datenum(year(curr_time),month(curr_time)-1,str2double(station_table{ss}),str2double(station_table{ss,2}(1:2)),str2double(station_table{ss,2}(4:5)),0)];                
            end
        else
            datenum_vec = [datenum_vec,datenum(year(curr_time),month(curr_time),str2double(station_table{ss}),str2double(station_table{ss,2}(1:2)),str2double(station_table{ss,2}(4:5)),0)];                            
        end
    end    
end
end

Finally, we have the x-variable ('datenum_vec') and y-variable ('var_pick'), along with the variable name ('var_name'), which can all be used to plot the data onto our figure above. The function shown above plots the data for a given column of data using the station table created in the program. To determine which column to plot, take a look at the table data, or just plot the variables based on the columns in the weather.gov METAR website. They should be identical. To plot the raw temperature values, the only lines of code needed are:

plt1 = plot(ax1,datenum_vec,var_pick);
fig1.Visible='on';

The plot is a simple line plot, and the visibility of the plot is set to visible once the data is plotted. The figure should now look something like this:

raw_temp_plot.png

Figure 6: Raw Temperature Plot

Not publication quality yet, but we're getting there!


Perfecting the MATLAB Plot Appearance

At this point, we have a fully functional plotting routine that grabs data from the internet (weather.gov METAR station data), decomposes its html codec into a MATLAB table, and plots a chosen variable into a nicely-formatted MATLAB figure. Now, we have a few tweaks and adjustments to make before we can call this 'publication quality.' Several issues are apparent: the line is too skinny, there are no labels or title, the x-axis has date numbers which speak nothing to the actual dates, and the limits on the x-axis are too arbitrary. Below is the fixed figure with the proper titles, axes, line width, and I even included scatter points to show the individual data points. 

temp_axis_title_600dpi.png

Figure 7: Upgraded Temperature Plot

Approaching publication quality with improved line thickness, axes labels, title, and limits, as well as meaningful x-axis tick labels.

Figure 7 is a good example of a near publication-quality visual. It has a resolution of 600 dpi (sometimes even higher is recommended), it has formatted axes, visible font sizes, colorful visuals with thick lines and markers, and a clear and concise title that allows the reader to know exactly what they are viewing. Depending on the journal or form of publication, the formatting or color palette may need to be altered. Some journals do not use color in their plots, some do not want color backgrounds, some have specific formatting for units, etc. The figure above is not particularly interesting, however it meets publication expectations in visual representation. Below is a more detailed plot showing the relative behavior between temperature and humidity.

temp_humiditiy_2_axis_title_600dpi.png

Figure 8: Temperature and Humidity 

Here we have a publication-quality figure that contains all the characteristics found in scientific research journals

The figure shown above, Figure 8, is a perfect example of the quality you might see in scientific research journals. As I mentioned above, the format may not fit the targeted journal's specifications, however, the basics are there. Some alterations could be made to the axes colors, or the time format on the x-axis, or the legend, or figure background color, or the limits - all of these are possibilities that are specific to both user and journal. The code below outlines the function used to produce the figure above:

function plot_algo(station_table,station_name_formal,home_station,var_select)
    legend_str = {};    
    for ii = 1:length(var_select) % loop through variables to ensure legend catches each
        [datenum_vec,var_pick,var_name] = parse_and_format(station_table,var_select(ii)); % send variable through parse function
        % plotting traits
        sz = 10; % size of marker
        color_map = lines; % colormap used for lines and markers
        if length(var_select)<=2 % this is particular to my dataset (METAR)
            switch ii
                case 1 % if only 1 variable, make 1 y-axis label
                    ylabel(var_name)
                case 2 % if two variables, make two y-axis labels
                    yyaxis right; % second y-axis to the right
                    y2 = gca;
                    ylabel(var_name)
                    y2.YColor = color_map(ii,:); % ensure the color of the axis matches the second line
            end % in my case, I'm chosing to discard the y-labels if more than 2 variables are entered
        end 
        % plot specifics: -s creates a square marker with a line
        plot(datenum_vec,var_pick,'-s','linewidth',3,'color',color_map(ii,:),...
            'MarkerFaceColor',(1-color_map(ii,:))/1.5+color_map(ii,:),'MarkerEdgeColor',[25 25 25]./256,...
            'MarkerSize',sz)
        % title handling for month and year
        curr_time = datetime(); 
        title(strcat('Weather for {}',station_name_formal,'{ }(',home_station,') in {}',datestr(curr_time,'mmmm, yyyy')))              
        xlabel('Time [Day HH:MM]')
        % chosing to step through x-values by 6 (72/6 = 12 x-ticks)
        xticks(datenum_vec(end:-6:1))
        datetick(gca,'x','dd HH:MM','keepticks')
        % rotating the x-ticks so they don't overlap
        xtickangle(20)
        % setting the limits
        xlim([datenum_vec(end) datenum_vec(1)])
        % appending the names to the legend handler
        legend_str{end+1} = var_name; 
    end
    % white background and letting MATLAB choose the best location for the
    % legend
    legend(legend_str,'Location','Best','Color','white')
end

Now, with all the tools above, one should be capable of producing a plot similar to the one above. The only hard coding the user needs to do is call the function as such:

plot_algo(station_table,station_name_formal,station_ID,var)

The function above needs four variables:

  1. Station table
  2. Station name 
  3. Station ID
  4. Weather variables to plot

For example, if you wanted to plot the temperature and dew point for Austin-Bergstrom International Airport (KAUS), the function needs to be formatted as follows:

plot_algo(station_table,'Austin-Bergstrom International Airport','KAUS',[7,8])

This produces the plot shown in Figure 9, below.

  Figure 9:&nbsp; Final representation of MATLAB's plotting capabilities using METAR data for an Austin, TX airport ('KAUS').&nbsp;

Figure 9: Final representation of MATLAB's plotting capabilities using METAR data for an Austin, TX airport ('KAUS'). 

If you'd like the full MATLAB code for a seamless script, you can download it here (you can also copy and paste the code directly, here). From there it should handle the rest of the work!


Conclusions

When it comes to exhibiting professional work, the human eye determines the scale of expectation. That is precisely why I meticulously craft representations of my research. Peer reviewed journals scour through manuscripts, which is why it is important to maintain excellence when publishing figures. Above, I demonstrated how to create powerful visuals in MATLAB by utilizing the METAR database. The tutorial showed how to take publicly available weather data, import and parse it into MATLAB, and produce high quality figures. MATLAB is a robust tool that, when used properly, can be used to generate publication-worthy plots to represent data across all fields.

NOTE: The figures shown above may appear fuzzy. This is because the images are automatically resized and reformatted by my web provider. Therefore, the 600 dpi resolution is not showing honored here, however, rest assured that MATLAB is capable of exporting resolutions of 600 dpi and greater. For a high resolution example, see here.