Main Content

Compositing and Animating Web Map Service (WMS) Meteorological Layers

This example shows how to composite and animate data from multiple Web Map Service (WMS) layers.

The base layer is from the NASA Goddard Space Flight Center's Scientific Visualization Studio (SVS) Image Server. The data in this layer shows satellite cloud data during Hurricane Katrina from August 23 through August 30, 2005. The layer consists of cloud data extracted from GOES-12 imagery and overlaid on a color image of the southeast United States.

Next-Generation Radar (NEXRAD) images, collected by the Iowa State University's Iowa Environmental Mesonet (IEM) Web map server, are composited with the cloud data at regular intervals of time.

In particular, this example will show you how to:

  • Use the WMS database to find the Katrina and NEXRAD layers

  • Retrieve the Katrina base map from a WMS server at a particular time-step

  • Retrieve the NEXRAD map from a WMS server at the same time-step

  • Composite the base map with the map containing the NEXRAD imagery

  • View the composited map in a projected coordinate system

  • Retrieve, composite, and animate multiple time sequences

  • Create a video file and animated GIF file of the animation

Understanding Basic WMS Terminology

If you are new to WMS, several key concepts are important to understand and are listed here.

  • Web Map Service --- The Open Geospatial Consortium (OGC) defines a Web Map Service (WMS) to be an entity that "produces maps of spatially referenced data dynamically from geographic information."

  • WMS server --- A server that follows the guidelines of the OGC to render maps and return them to clients

  • map --- The OGC definition for map is "a portrayal of geographic information as a digital image file suitable for display on a computer screen."

  • layer --- A dataset of a specific type of geographic information, such as temperature, elevation, weather, orthophotos, boundaries, demographics, topography, transportation, environmental measurements, and various data from satellites

  • capabilities document --- An XML document containing metadata describing the geographic content offered by a server

Internet Access

Since WMS servers are located on the Internet, this example can be set to access the Internet to dynamically render and retrieve maps from WMS servers or it can be set to use data previously retrieved from the Internet using the WMS capabilities but now stored in local files. You can use a variable, useInternet, to determine whether to read data from locally stored files, or retrieve the data from the Internet.

If the useInternet flag is set to true, then an Internet connection must be established to run the example. Note that the WMS servers may be unavailable, and several minutes may elapse before the maps are returned. One of the challenges of working with WMS servers is that sometimes you will encounter server errors. A function, such as wmsread, may time out if a server is unavailable. Often, this is a temporary problem and you will be able to connect to the server if you try again later.

This example writes data to files if useInternet is true or reads data from files if useInternet is false. You can store the data locally the first time you run the example and then set the useInternet flag to false.

useInternet = false;

Step 1: Find Katrina Layers From Local Database

One of the more challenging aspects of using WMS is finding a WMS server and then finding the layer that is of interest to you. The process of finding a server that contains the data you need and constructing a specific and often complicated URL with all the relevant details can be very daunting.

The Mapping Toolbox™ simplifies the process of locating WMS servers and layers by providing a local, installed, and pre-qualified WMS database, that is searchable, using the function wmsfind. You can search the database for layers and servers that are of interest to you. Here is how you find layers containing the term katrina in either the LayerName or LayerTitle field of the database:

katrina = wmsfind('katrina');
whos katrina
  Name          Size            Bytes  Class       Attributes

  katrina      34x1             16754  WMSLayer              

The search for the term 'katrina' returned a WMSLayer array containing multiple layers. To inspect information about an individual layer, simply display it like this:

katrina(1)
ans = 
  WMSLayer

  Properties:
           Index: 1
     ServerTitle: 'NASA SVS Image Server'
       ServerURL: 'https://svs.gsfc.nasa.gov/cgi-bin/wms?'
      LayerTitle: 'GOES-12 Imagery of Hurricane Katrina: Longwave Infrared Close-up (1024x1024 Animation)'
       LayerName: '3216_22510'
          Latlim: [15.0000 45.0000]
          Lonlim: [-100.0000 -70.0000]

  Methods

If you type, katrina, in the command window, the entire contents of the array are displayed, with each element's index number included in the output. This display makes it easy for you to examine the entire array quickly, searching for a layer of interest. You can display only the LayerTitle property for each element by executing the command:

disp(katrina,'Properties','layertitle','Index','off','Label','off');

As you've discovered, a search for the generic word 'katrina' returned results of many layers and you need to select only one layer. In general, a search may even return thousands of layers, which may be too large to review individually. Rather than searching the database again, you may refine your search by using the refine method of the WMSLayer class. Using the refine method is more efficient and returns results faster than wmsfind since the search has already been narrowed to a smaller set. Supplying the query string, 'goes-12*katrina*visible*close*up*animation', to the refine method returns a WMSLayer array whose elements contain a match of the query string in either the LayerTitle or LayerName properties. The * character indicates a wild-card search. If multiple entries are returned, select only the first one from the svs.gsfc.nasa.gov server.

katrina = refine(katrina,'goes-12*katrina*visible*close*up*animation');
katrina = refine(katrina,'svs.gsfc.nasa.gov','Searchfield','serverurl');
katrina = katrina(1);
whos katrina
  Name         Size            Bytes  Class       Attributes

  katrina      1x1               466  WMSLayer              

Step 2: Synchronize WMSLayer Object with Server

The database only stores a subset of the layer information. For example, information from the layer's abstract, details about the layer's attributes and style information, and the coordinate reference system of the layer are not returned by wmsfind. To return all the information, you need to use the wmsupdate function. wmsupdate synchronizes the layer from the database with the server, filling in the missing properties of the layer.

Synchronize the first katrina layer with the server in order to obtain the abstract information. Since this action requires access to the Internet, call wmsupdate only if the useInternet flag is true.

if useInternet
    katrina = wmsupdate(katrina);
    save('katrina.mat','katrina')
else
    load('katrina.mat')
end

Display the abstract information of the layer. Use isspace to help determine where to line wrap the text.

abstract = katrina.Abstract;
endOfLine = find(isstrprop(abstract,'cntrl'),1);
abstract = abstract(1:endOfLine);
numSpaces = 60;
while(~isempty(abstract))
    k = find(isspace(abstract));
    n = find(k > numSpaces,1);
    if ~isempty(n)
        fprintf('%s\n',abstract(1:k(n)))
        abstract(1:k(n)) = [];
    else
        fprintf('%s\n',abstract)
        abstract = '';
    end
end
The GOES-12 satellite sits at 75 degrees west longitude at an 
altitude of 36,000 kilometers over the equator, in geosynchronous 
orbit.  At this position its Imager instrument takes pictures 
of cloud patterns in several wavelengths for all of North and 
South America, a primary measurement used in weather forecasting. 
 The Imager takes a pattern of pictures of parts of the Earth 
in several wavelengths all day, measurements that are vital in 
weather forecasting.  This animation shows a daily sequence of 
GOES-12 images in the visible wavelengths, from 0.52 to 0.72 
microns, during the period that Hurricane Katrina passed through 
the Gulf of Mexico.  At one kilometer resolution, the visible 
band measurement is the highest resolution data from the Imager, 
which accounts for the very high level of detail in these images. 
 For this animation, the cloud data was extracted from GOES image 
and laid over a background color image of the southeast United 
States.

Note that this abstract information, including any typographical issues and incomplete fragments, was obtained directly from the server.

Step 3: Explore Katrina Layer Details

You can find out more information about the katrina layer by exploring the Details property of the katrina layer. The Details.Attributes field informs you that the layer has fixed width and fixed height attributes, thus the size of the requested map cannot be modified.

katrina.Details.Attributes
ans = struct with fields:
      Queryable: 0
       Cascaded: 0
         Opaque: 1
      NoSubsets: 1
     FixedWidth: 1024
    FixedHeight: 1024

The Details.Dimension field informs you that the layer has a time dimension

katrina.Details.Dimension
ans = struct with fields:
              Name: 'time'
             Units: 'ISO8601'
        UnitSymbol: ''
           Default: '2005-08-30T17:45Z'
    MultipleValues: 0
      NearestValue: 0
           Current: 0
            Extent: '2005-08-23T17:45Z/2005-08-30T17:45Z/P1D'

with an extent from 2005-08-23T17:45Z to 2005-08-30T17:45Z with a period of P1D (one day), as shown in the Details.Dimension.Extent field.

katrina.Details.Dimension.Extent
ans = 
'2005-08-23T17:45Z/2005-08-30T17:45Z/P1D'

Step 4: Retrieve Katrina Map from Server

Now that you have found a layer of interest, you can retrieve the raster map using the function wmsread and display the map using the function geoshow. Since Time is not specified when reading the layer, the default time, 2005-08-30T17:45Z, is retrieved as specified by the Details.Dimension.Default field. If the useInternet flag is set to true, then cache the image and reference object in a GeoTIFF file.

if useInternet
    [katrinaMap,R] = wmsread(katrina);
    geotiffwrite('katrina.tif',katrinaMap,R)
else
    [katrinaMap,R] = readgeoraster('katrina.tif');
end

Display the katrinaMap and overlay the data from the usastatehi.shp file.

states = readgeotable('usastatehi.shp');
figure
usamap(katrina.Latlim, katrina.Lonlim)
geoshow(katrinaMap,R)
geoshow(states,'FaceColor','none')
title({katrina.LayerTitle, katrina.Details.Dimension.Default}, ...
    'Interpreter','none')

Step 5: Find NEXRAD Radar Layer

NEXRAD radar images for the United States are stored on the Iowa State University's IEM Web map server. The server conveniently stores NEXRAD images in five minute increments from 1995-01-01 to the present time. You can find the layer by first searching for the term IEM WMS Service in the ServerTitle field of the WMS database, then refining the search by requesting the layer of interest, nexrad-n0r-wmst.

iemLayers = wmsfind('IEM WMS Service','SearchField','servertitle');
nexrad = refine(iemLayers,'nexrad-n0r-wmst');

Synchronize the layer with the server.

if useInternet
    nexrad = wmsupdate(nexrad);
    save('nexrad.mat','nexrad')
else
    load('nexrad.mat');
end

Step 6: Obtain Extent Parameters

To composite the nexrad layer with the katrina layer, you need to obtain the nexrad layer at coincidental time periods, and concurrent geographic and image extents. The Details.Dimension field informs you that the layer has a time dimension,

nexrad.Details.Dimension
ans = struct with fields:
              Name: 'time'
             Units: 'ISO8601'
        UnitSymbol: ''
           Default: '2006-06-23T03:10:00Z'
    MultipleValues: 0
      NearestValue: 0
           Current: 0
            Extent: '1995-01-01/2022-12-31/PT5M'

and the Details.Dimension.Default field informs you that the layer's time extent includes seconds.

nexrad.Details.Dimension.Default
ans = 
'2006-06-23T03:10:00Z'

Obtain a time value coincidental with the katrina layer, and add seconds to the time specification.

nexradTime = [katrina.Details.Dimension.Default(1:end-1) ':00Z'];

Assign latlim and lonlim variables to specify the limits for the nexrad layer. Set the values to the limits of the katrina layer so that the geographic areas match. Note that the nexrad layer's southern latitude limit does not extend as far south as the katrina layer's southern latitude limit. The values that lie outside the geographic bounding quadrangle of the nexrad layer are set to the background color.

fprintf('%s%d\n','Southern latitude limit of NEXRAD  layer: ',nexrad.Latlim(1))
Southern latitude limit of NEXRAD  layer: 24
fprintf('%s%d\n','Southern latitude limit of Katrina layer: ',katrina.Latlim(1))
Southern latitude limit of Katrina layer: 10
latlim = katrina.Latlim;
lonlim = katrina.Lonlim;

Assign imageHeight and imageWidth variables.

imageHeight = katrina.Details.Attributes.FixedHeight;
imageWidth  = katrina.Details.Attributes.FixedWidth;

Step 7: Retrieve NEXRAD Radar Map from Server

You can retrieve the nexradMap from the server, specified at the same time as the katrinaMap and for the same geographic and image extents, by supplying parameter/value pairs to the wmsread function. To accurately retrieve the radar signal from the map, set the ImageFormat parameter to the image/png format. In order to easily retrieve the signal from the background, set the background color to black ([0 0 0]).

Retrieve the nexradMap.

black = [0 0 0];
if useInternet
    [nexradMap,R] = wmsread(nexrad, ...
        'Latlim',latlim,'Lonlim',lonlim,'Time',nexradTime, ...
        'BackgroundColor',black,'ImageFormat','image/png', ...
        'ImageHeight',imageHeight,'ImageWidth',imageWidth);
    geotiffwrite('nexrad.tif',nexradMap,R)
else
    [nexradMap,R] = readgeoraster('nexrad.tif');
end

Display the nexradMap.

figure
usamap(latlim,lonlim)
geoshow(nexradMap,R)
geoshow(states,'FaceColor','none','EdgeColor',[0.9 0.9 0.9])
title({nexrad.LayerTitle, nexradTime},'Interpreter','none');

Step 8: Composite NEXRAD Radar Map with Katrina Map

To composite the nexradMap with a copy of the katrinaMap, you need to identify the non-background pixels in the nexradMap. The nexradMap data is returned as an image with class double, because of how this web map server handles PNG format, so you need convert it to uint8 before merging.

Identify the pixels of the nexradMap image that do not contain the background color.

threshold = 0;
index = any(nexradMap > threshold, 3);
index = repmat(index,[1 1 3]);

Composite the nexradMap with the katrinaMap.

combination = katrinaMap;
combination(index) = uint8(nexradMap(index)*255);

Display the composited map.

figure
usamap(latlim,lonlim)
geoshow(combination,R)
geoshow(states,'FaceColor','none')
title({'GOES 12 Imagery of Hurricane Katrina', ...
    'Composited with NEXRAD Radar',nexradTime})

Step 9: Initialize Variables to Animate the Katrina and NEXRAD Maps

The next step is to initialize variables in order to animate the composited katrina and nexrad maps.

Create variables that contain the time extent of the katrina layer.

extent = katrina.Details.Dimension.Extent;
slash = '/';
slashIndex = strfind(extent,slash);
startTime = extent(1:slashIndex(1)-1);
endTime = extent(slashIndex(1)+1:slashIndex(2)-1);

Calculate numeric values for the start and end days. Note that the time extent is in yyyy-mm-dd format.

hyphen = '-';
hyphenIndex = strfind(startTime,hyphen);
dayIndex = [hyphenIndex(2) + 1, hyphenIndex(2) + 2];
startDay = str2double(startTime(dayIndex));
endDay = str2double(endTime(dayIndex));

Assign the initial katrinaTime.

katrinaTime = startTime;

Since multiple requests to a server are required for animation, it is more efficient to use the WebMapServer and WMSMapRequest classes.

Construct a WebMapServer object for each layer's server.

nasaServer = WebMapServer(katrina.ServerURL);
iemServer  = WebMapServer(nexrad.ServerURL);

Create WMSMapRequest objects.

katrinaRequest = WMSMapRequest(katrina, nasaServer);
nexradRequest  = WMSMapRequest(nexrad, iemServer);

Assign properties.

nexradRequest.Latlim = latlim;
nexradRequest.Lonlim = lonlim;
nexradRequest.BackgroundColor = black;
nexradRequest.ImageFormat = 'image/png';
nexradRequest.ImageHeight = imageHeight;
nexradRequest.ImageWidth  = imageWidth;

Step 10: Create Animation Files

An animation can be viewed in the browser when the browser opens an animated GIF file or an AVI video file. To create the animation frames of the WMS basemap and vector overlays, create a loop through each day, from startDay to endDay, and obtain the katrinaMap and the nexradMap for that day. Composite the maps into a single image, display the image, retrieve the frame, and store the results into a frame of an AVI file and a frame of an animated GIF file.

To share with others or to post to web video services, create an AVI video file containing all the frames using the VideoWriter class.

videoFilename = fullfile(pwd,'wmsanimated.avi');
if exist(videoFilename,'file')
    delete(videoFilename)
end
writer = VideoWriter(videoFilename);
writer.FrameRate = 1;
writer.Quality = 100;
open(writer)

The animation is viewed in a single map display. Outside the animation loop, create a map display. Initialize hmap, used in the loop as the return handle from the function geoshow, so it can be deleted on the first pass through the loop. Loop through each day, retrieve and display the WMS map, and save the frame.

fig = figure;
usamap(latlim,lonlim)
hstates = geoshow(states,'FaceColor','none');
hmap = [];

for k = startDay:endDay
    
    % Update the time values and assign the Time property for each server.
    currentDay = num2str(k);
    katrinaTime(dayIndex) = currentDay;
    nexradTime = [katrinaTime(1:end-1) ':00Z'];
    katrinaRequest.Time = katrinaTime;
    nexradRequest.Time = nexradTime;
    
    % Retrieve the WMS map of Katrina from the server (or file)
    % for this time period.
    nmKatrina = ['katrina_' num2str(currentDay) '.tif'];
    if useInternet
        katrinaMap = getMap(nasaServer, katrinaRequest.RequestURL);
        geotiffwrite(nmKatrina, katrinaMap, katrinaRequest.RasterRef)
    else
        katrinaMap = readgeoraster(nmKatrina);
    end
    
    % Retrieve the WMS map of the NEXRAD imagery from the server (or file)
    % for this time period.
    nmNEXRAD = ['nexrad_' num2str(currentDay) '.tif'];
    if useInternet
        nexradMap = getMap(iemServer, nexradRequest.RequestURL);
        geotiffwrite(nmNEXRAD, nexradMap, nexradRequest.RasterRef)
    else
        nexradMap = readgeoraster(nmNEXRAD);
    end
    
    % Identify the pixels of the nexradMap image that do not contain the
    % background color.
    index = any(nexradMap > threshold, 3);
    index = repmat(index,[1 1 3]);
    
    % Composite nexradMap with katrinaMap.
    combination = katrinaMap;
    combination(index) = uint8(nexradMap(index)*255);
    
    % Delete the old map and display the new composited map.
    delete(hmap)
    hmap = geoshow(combination, katrinaRequest.RasterRef);
    uistack(hstates,'top')
    title({'GOES 12 Imagery of Hurricane Katrina', ...
        'Composited with NEXRAD Radar',nexradTime})
    drawnow
    
    % Save the current frame as an RGB image.
    currentFrame = getframe(fig);
    RGB = currentFrame.cdata;
    
    % Create an indexed image for each RGB frame in order to display an
    % animated GIF. 
    if k == startDay
        % The first time through the loop, convert the RGB image to
        % an indexed image and save the colormap into the
        % variable, cmap. Use cmap to convert later frames.
        [frame,cmap] = rgb2ind(RGB,256,'nodither');
        
        % Use the size of the first frame and the total
        % number of frames to initialize animated with
        % a size large enough to contain all the frames.
        frameSize = size(frame);
        numFrames = endDay - startDay + 1;
        animated = zeros([frameSize 1 numFrames],'like',frame);
    else
        % Use the colormap from the first frame conversion and
        % convert this frame to an indexed image.
        frame = rgb2ind(RGB,cmap,'nodither');
    end
   
    % Store the frame into the animated array for the GIF file.
    frameCount = k - startDay + 1;
    animated(:,:,1,frameCount) = frame;
    
    % Write the RGB frame to the AVI file.
    writeVideo(writer,RGB);
end

Close the Figure window and the AVI file.

close(fig)
close(writer)

Write the animated GIF file.

filename = 'wmsanimated.gif';
delayTime = 2.0;
loopCount = inf;
imwrite(animated,cmap,filename, ...
    'DelayTime',delayTime,'LoopCount',loopCount);

Step 11: View Animated GIF File

An animation can be viewed in the browser when the browser opens an animated GIF file.

wmsanimated.gif

Credits

Katrina Layer

The Katrina layer used in the example is from the NASA Goddard Space Flight Center's SVS Image Server and is maintained by the Scientific Visualization Studio.

For more information about this server, use the command wmsinfo('http://svs.gsfc.nasa.gov/cgi-bin/wms?').

NEXRAD Layer

The NEXRAD layer used in the example is from the Iowa State University's IEM WMS server and is a generated CONUS composite of National Weather Service (NWS) WSR-88D level III base reflectivity.

For more information about this server, use the command wmsinfo('http://mesonet.agron.iastate.edu/cgi-bin/wms/nexrad/n0r-t.cgi?').

See Also

Functions

Objects