Tile Pyramid

Image from Web Map Tile Services for Spatial Data Infrastructures: Management and Optimization

Web Map Services

Web map services (WMS) provide small, pre-rendered image tiles that collectively form a larger map when displayed in a grid-like pattern. Each tile represents a specific geographic area and zoom level, allowing users to view maps at different scales and locations.

Tile Specifications

Various specifications govern the functionality of web map services, including TMS (Tile Map Service), WMTS (Web Map Tile Service), and XYZ. These specifications define how the tiles are organized, accessed, and displayed within a web mapping context.

However, it is not necessary to delve into the technical details of these underlying specifications in order to benefit from web map services. The implementation takes care of these intricacies behind the scenes, allowing users to seamlessly utilize the service without requiring in-depth knowledge.

Nonetheless, it is useful to have an understanding of the format that each specification follows. Here are examples of URL patterns associated with each specification:

TMS (Tile Map Service): http://example.com/tiles/{z}/{x}/{y}.png WMTS (Web Map Tile Service): http://example.com/wmts/{Layer}/{TileMatrixSet}/{TileMatrix}/{TileRow}/{TileCol}.png XYZ: http://example.com/{z}/{x}/{y}.png

These URLs represent the structure for accessing individual map tiles based on their zoom level (z), column (x), and row (y) coordinates, and sometimes, as you’ll see later, it’s based on the bounds {XMIN},{XMAX},{YMIN},{YMAX}. Other specifications exist as well, such as Quadkey used by Bing Maps and GeoPackage for storing tiles in a single file.

Online WMS Viewers

Online WMS viewers provide a user-friendly interface for accessing and visualizing geospatial data through web map services. They offer a convenient way to explore and interact with various layers of information, ranging from weather data to satellite imagery.

Here are some examples of online WMS viewers:

NOAA Weather Viewer (https://viewer.weather.noaa.gov/): This viewer, provided by the National Oceanic and Atmospheric Administration (NOAA), allows users to access and visualize real-time weather information, including radar data, forecasts, and satellite imagery. It offers an intuitive interface for exploring weather conditions and monitoring meteorological phenomena.

National Map Viewer (https://apps.nationalmap.gov/viewer/): The National Map Viewer, developed by the United States Geological Survey (USGS), provides access to a wide range of geospatial data, including topographic maps, aerial imagery, and elevation data. Users can explore and overlay different datasets, enabling them to analyze and visualize various geographic features.

NASA Worldview (https://worldview.earthdata.nasa.gov/): NASA Worldview is a powerful online WMS viewer that offers access to a vast collection of satellite imagery and Earth observation data. Users can view imagery captured by different satellite missions and explore global environmental changes over time. The viewer supports advanced visualization and analysis tools, allowing users to study phenomena such as atmospheric conditions, land cover changes, and natural disasters.

While there are existing online WMS viewers that provide access to a wide range of geospatial data, the true power of web map services (WMS) lies in the ability to overlay them with your own data.

We will explore how to do this below.

Prerequisites

The following packages are good to know, but not required, as we will be walking through them below.

Concepts

Importance

Notes

Intro to GeoViews

Helpful

Geographic visualizations

Intro to Panel

Helpful

Dashboard creations

Intro to Requests

Helpful

Webpage requests

Intro to Cartopy

Helpful

Map projections

Intro to OWSLib

Helpful

WMS URLs

  • Time to learn: 15 minutes


Imports

Let’s first import a few packages.

GeoViews is a Python library that facilitates the integration of WMS and other geospatial data sources with your own datasets. It provides a high-level interface for working with geographic data and simplifies the process of creating interactive visualizations.

CartoPy is a Python library that specializes in handling map projections and geospatial data visualization. It provides a wide range of tools and functionalities to work with geospatial datasets, making it easier to create maps with different projections.

Panel is a Python library that offers a set of flexible and powerful tools for creating interactive dashboards and apps. It allows you to build custom user interfaces with interactive controls, widgets, and layout components, enabling rich interactivity for your visualizations and data analysis workflows.

Requests is a popular Python library for making HTTP requests. It simplifies sending HTTP requests to web servers and handling the responses. In the context of geospatial data visualization, requests can be used to retrieve data from web services, such as WMS endpoints, to integrate external data sources into your visualizations.

OWSLib is a Python library designed for client-side programming using the interface standards of the Open Geospatial Consortium (OGC) web services and their associated content models. Specifically, in this scenario, OWSLib will be utilized solely for the purpose of constructing URLs for WMS.

The next line, gv.extension("bokeh"), enables the Bokeh (interactive) plotting backend for GeoViews. GeoViews supports multiple plotting backends, such as Bokeh and Matplotlib, which allow you to choose the one that best suits your needs.

Finally, pn.extension() initializes the panel library and sets up the necessary environment for creating interactive panels and dashboards. You may specify configurations like sizing_mode="stretch_width" within pn.extension().

import geoviews as gv
import cartopy.crs as ccrs
import panel as pn
import requests
from owslib.wms import WebMapService

gv.extension("bokeh")
pn.extension()
/home/runner/miniconda3/envs/cookbook-dev/lib/python3.10/site-packages/dask/dataframe/__init__.py:31: FutureWarning: 
Dask dataframe query planning is disabled because dask-expr is not installed.

You can install it with `pip install dask[dataframe]` or `conda install dask`.
This will raise in a future version.

  warnings.warn(msg, FutureWarning)

Using Built-in Sources

Here’s how to use GeoViews to access built-in WMS tile sources effortlessly.

By activating the “pan” and “wheel_zoom” tools, users gain interactive control over the map. They can easily navigate the map by dragging, exploring different areas, and experiencing a sense of flexibility and control.

To experience the capabilities of WMS and GeoViews, try zooming in on the map. As you zoom in, you will notice that more details and finer features become visible, providing a richer and more detailed representation of the geographic data.

To see all available built-in tile sources, please visit the GeoViews documentation.

basemap = gv.tile_sources.OSM().opts(active_tools=["pan", "wheel_zoom"])
basemap

Overlaying Custom Data

By integrating WMS layers with your own data, you can unlock the potential of geospatial information and create maps that communicate intricate spatial relationships, highlight patterns and trends, and offer valuable insights.

Let’s illustrate this with an example. In isolation, the coordinates (40.1N, -88.26W) may not hold much significance. However, when overlaying the WMS tiles and zooming in, you might discover that these coordinates correspond to the location of the University of Illinois at Urbana-Champaign, the author’s alma mater. This simple overlay demonstrates how WMS layers can provide context and meaningful information to otherwise isolated data points.

Note that crs and projection were specified.

The crs=ccrs.PlateCarree() argument specifies that the data coordinates are from the Plate Carree coordinate reference system, which is commonly used for latitude and longitude values.

On the other hand, the projection parameter determines what the data should be transformed to before being displayed.

At the time of writing, GeoViews only supports overlaying data with tile sources when the map’s projection is set to ccrs.GOOGLE_MERCATOR.

Lastly, the global_extent=True option ensures that the map displays the entire world.

coord_point = gv.Points((-88.24, 40.1), crs=ccrs.PlateCarree()).opts(projection=ccrs.GOOGLE_MERCATOR, global_extent=True)

basemap * coord_point

Accessing External Sources

Although the availability of built-in sources is convenient, it may not always be sufficient for all use cases. However, the good news is that there are numerous external sources available, such as NOAA, which can be easily accessed, and depending on the source, free of charge!

For example, if you want to access radar base reflectivity provided by NOAA, you can create a gv.WMTS element with the appropriate URL and parameters specific to the external tile service.

radar_url_template = """
https://mapservices.weather.noaa.gov:443/eventdriven/services/
radar/radar_base_reflectivity_time/ImageServer/WMSServer?
service=WMS&version=1.1.1&request=GetMap&
layers=0&
styles=default&
width=256&height=256&
srs=EPSG%3A3857&
bbox={XMIN}%2C{YMIN}%2C{XMAX}%2C{YMAX}&
format=image%2Fpng&transparent=TRUE&
exceptions=application%2Fvnd.ogc.se_xml&bgcolor=0xFFFFFF
"""

radar_imagery = gv.WMTS(radar_url_template, crs=ccrs.GOOGLE_MERCATOR).opts(projection=ccrs.GOOGLE_MERCATOR, global_extent=True)

basemap * radar_imagery

Understanding the URL Template

The provided URL template is used to construct a request for a specific map image using WMS protocol.

Let’s break down the components of the URL to understand its functionality:

  1. https://mapservices.weather.noaa.gov:443/eventdriven/services/ This is the base URL for the NOAA weather map services. It specifies the server address and port.

  2. radar/radar_base_reflectivity_time/ImageServer/WMSServer? This part of the URL specifies the specific service and resource being accessed. In this case, it indicates the radar_base_reflectivity_time image service within the radar category.

  3. service=WMS&version=1.1.1&request=GetMap These parameters define the service type (WMS) and the version of the WMS protocol being used (1.1.1). It also specifies that the request is to retrieve a map image (GetMap).

  4. layers=0 This parameter indicates the layer or layers from which the map image should be retrieved. In this case, it specifies layer 0.

  5. styles=default This parameter specifies the style to be applied to the map image. Here, it is set to the default style.

  6. width=256&height=256 These parameters define the width and height (in pixels) of the requested map image.

  7. srs=EPSG%3A3857 This parameter specifies the spatial reference system (SRS) for the map image. In this case, it uses the EPSG:3857 coordinate reference system.

  8. bbox={XMIN}%2C{YMIN}%2C{XMAX}%2C{YMAX} This parameter specifies the bounding box for the requested map image. The values {XMIN}, {YMIN}, {XMAX}, and {YMAX} will be replaced with actual coordinate values. The values provided within the curly braces are percent-encoded.

  9. format=image%2Fpng&transparent=TRUE These parameters define the desired image format (PNG) and specify that the image should have a transparent background.

  10. exceptions=application%2Fvnd.ogc.se_xml&bgcolor=0xFFFFFF These parameters define how exceptions should be reported (in XML format) and set the background color for the map image to white (#FFFFFF).

You might also be wondering what those percent-encoded characters, like %2C, mean.

Percent-encoding is a mechanism used to represent special and reserved characters in URLs by replacing them with a “%” symbol followed by their hexadecimal representation. It ensures safe transmission and proper interpretation of URLs containing characters with special meanings or non-ASCII characters.

To de-mystify the template URL a step further, we can simply use requests.utils.unquote and see:

  • %3A: Represents the colon character (:)

  • %2C: Represents the comma character (,)

  • %2F: Represents the forward slash character (/)

  • %20: Represents the space character

requests.utils.unquote(radar_url_template).strip()
'https://mapservices.weather.noaa.gov:443/eventdriven/services/\nradar/radar_base_reflectivity_time/ImageServer/WMSServer?\nservice=WMS&version=1.1.1&request=GetMap&\nlayers=0&\nstyles=default&\nwidth=256&height=256&\nsrs=EPSG:3857&\nbbox={XMIN},{YMIN},{XMAX},{YMAX}&\nformat=image/png&transparent=TRUE&\nexceptions=application/vnd.ogc.se_xml&bgcolor=0xFFFFFF'

Deriving the URL Template

Once you have understood the individual components of the URL template, you may wonder if you have to manually assemble them yourself. Fortunately, you can use OWSLib to programmatically construct the complete URL template.

To start, you need to locate the base URL and the specific resource manually by accessing the desired service and resource in your browser.

  1. Open weather.gov CloudGISWebServices

  2. In the table, click Base Reflectivity Radar

  3. On the top left, click WMS

  4. Copy and paste the browser URL into get_wms_url below.

  5. Run this cell and the working URL should be generated.

Note, some base_resource_urls feature multiple layers. Here, only the first layer is returned.

# Global extent in EPSG:3857 (Google Mercator)
XMIN = -20037507.539400
YMIN = 1638517.444800
XMAX = 20037260.918700
YMAX = 7714669.394600


def get_wms_url(base_resource_url):
    wms = WebMapService(base_resource_url)
    wms_contents = wms.contents
    wms_layer = list(wms_contents)[0]
    wms_styles = list(wms[wms_layer].styles)
    wms_url = wms.getmap(
        layers=[wms_layer],
        styles=wms_styles,
        srs="EPSG:3857",
        bbox=(XMIN, YMIN, XMAX, YMAX),
        size=(256, 256),
        format="image/png",
        transparent=True,
    ).geturl()
    return wms_url


radar_url = get_wms_url(
    "https://mapservices.weather.noaa.gov/eventdriven/services/radar/radar_base_reflectivity_time/ImageServer/WMSServer?request=GetCapabilities&service=WMS"
)
radar_url
'https://mapservices.weather.noaa.gov:443/eventdriven/services/radar/radar_base_reflectivity_time/ImageServer/WMSServer?service=WMS&version=1.1.1&request=GetMap&layers=0&styles=default&width=256&height=256&srs=EPSG%3A3857&bbox=-20037507.5394%2C1638517.4448%2C20037260.9187%2C7714669.3946&format=image%2Fpng&transparent=TRUE&exceptions=application%2Fvnd.ogc.se_xml&bgcolor=0xFFFFFF'

If you click on this URL, you should a tile like below!

pn.pane.Image(requests.get(radar_url).content, styles={"background": "white"})

However, we’re not done yet! If we simply pass this URL to gv.WMTS it will result in the following error:

ValueError: Tile source URL format not recognized. Must contain {X}/{Y}/{Z}, {XMIN}/{XMAX}/{YMIN}/{YMAX} or {Q} template strings.

Therefore, we must replace the bounding box values with these template strings.

def formulate_url_template(url):
    return (
        url
        .replace(str(XMIN), "{XMIN}")
        .replace(str(YMIN), "{YMIN}")
        .replace(str(XMAX), "{XMAX}")
        .replace(str(YMAX), "{YMAX}")
    )

radar_url_template = formulate_url_template(radar_url)

radar_url_template
'https://mapservices.weather.noaa.gov:443/eventdriven/services/radar/radar_base_reflectivity_time/ImageServer/WMSServer?service=WMS&version=1.1.1&request=GetMap&layers=0&styles=default&width=256&height=256&srs=EPSG%3A3857&bbox={XMIN}%2C{YMIN}%2C{XMAX}%2C{YMAX}&format=image%2Fpng&transparent=TRUE&exceptions=application%2Fvnd.ogc.se_xml&bgcolor=0xFFFFFF'

Now, we can pass the URL template to gv.WMTS and get the same result as before.

gv.WMTS(radar_url_template)

Troubleshooting URLs

If you encounter a blank page as a result, try formatting the URL template using the global XMIN, YMIN, XMAX, YMAX values in Mercator coordinates.

If it returns a white canvas, it probably means that there aren’t any issues–there’s simply no ongoing event, e.g. no precipitation for the radar to pick up on.

If you receive a detailed error message, that’s good news–you can make the necessary revisions.

It is crucial to emphasize once more that at the time of writing, gv.WMTS only works with the ccrs.GOOGLE_MERCATOR coordinate reference system/projection!

Getting Legends

A map loses its usefulness without a legend, unless the colors or symbols used are widely recognized.

As an example, let’s retrieve the legend for the CPC 6-10 Days Outlook map and modify the get_wms_url function above!

We can, once again depend on OWSLib to retrieve the legend!

def get_wms_urls(base_resource_url):
    wms = WebMapService(base_resource_url)
    wms_contents = wms.contents
    wms_layer = list(wms_contents)[0]
    wms_styles = list(wms[wms_layer].styles)
    wms_url = wms.getmap(
        layers=[wms_layer],
        styles=wms_styles,
        srs="EPSG:3857",
        bbox=(XMIN, YMIN, XMAX, YMAX),
        size=(256, 256),
        format="image/png",
        transparent=True,
    ).geturl()
    legend_url = wms_contents[wms_layer].styles[wms_styles[0]]["legend"]
    return {"wms": wms_url, "legend": legend_url, "title": wms_contents[wms_layer].title}


cpc_outlook_wms_urls = get_wms_urls(
    "https://mapservices.weather.noaa.gov/vector/services/outlooks/cpc_6_10_day_outlk/MapServer/WMSServer?request=GetCapabilities&service=WMS"
)
cpc_outlook_wms_url_template = formulate_url_template(cpc_outlook_wms_urls["wms"])
cpc_outlook_legend_url = cpc_outlook_wms_urls["legend"]
cpc_outlook_title = cpc_outlook_wms_urls["title"]

cpc_outlook_imagery = gv.WMTS(cpc_outlook_wms_url_template).opts(global_extent=True, title=cpc_outlook_title)
cpc_outlook_legend_image = pn.pane.Image(requests.get(cpc_outlook_legend_url).content)

pn.Row(
    basemap * cpc_outlook_imagery,
    cpc_outlook_legend_image,
)

Summary

Web map services (WMS) provide pre-rendered map tiles for displaying geospatial data.

They can be accessed through URLs based on specifications like TMS, WMTS, and XYZ. Online WMS viewers offer user-friendly interfaces for exploring geospatial data.

However, the true power of WMS lies in overlaying them with your own data. Packages like GeoViews, Panel, Requests, and Cartopy facilitate this integration.

For more examples of how to use WMS, check out the example workflows or refer to the documentation for OWSLib.

Next, we will explore how to use web feature services.