JavaScript map libraries with WMS Radar data
In a recent post, we discussed the Canadian Government’s open platform to share Meteorological data, MSC GeoMet. MSC GeoMet provides public access to the Meteorological Service of Canada (MSC) and Environment and Climate Change Canada (ECCC) data via interoperable web services and application programming interfaces (API).
Web Map Services
GeoMet provides direct access to meteorological data via Web Map Services. If you aren’t familiar with Web Map Services (WMS), it is a standard protocol for serving georeferenced maps over the Internet. These services are easily consumed in many web map libraries to build your mapping websites and applications. In our examples, we will use the GeoMet service and the main map request type, GetMap, to talk to the WMS server. Some of the JavaScript libraries also leverage GetCapabilities requests, but since we already know what layers we want to use, we don’t need to specify both requests. Both of these Get requests are required by WMS servers:
-
GetCapabilities – returns parameters about the WMS (such as map image format and WMS version compatibility) and the available layers (map bounding box, coordinate reference systems, URI of the data and whether the layer is mostly opaque or not)
-
GetMap – returns a map image. Parameters include: width and height of the map, coordinate reference system, rendering style, image format
When a map is published as a WMS, GetCapabilities is typically used to discover what is available in the service. The layers, symbology, time intervals, and metadata can be searched. Once you know what you want, you can request a map image using the GetMap request with some parameters to describe what you are interested in. Most of the GetMap parameters are handled by the JavaScript Library and are automatically configured. When panning and zooming on the map, the library will update the coordinate parameters and send a new request to update the map.
Building a Web Map
The goal is simple - Add the WMS data to many different web mapping API/frameworks. I hope these examples are easy to understand, as the steps taken will work with most WMS endpoints. We could get more advanced and query the time slices available in the radar datasets, but let’s start with a basic Hello Map for some of the more popular javascript mapping libraries: Leaflet, ArcGIS JavaScript API, and OpenLayers.
Keeping the examples simple and with similar requirements:
- Map: Basic full-screen map
- Set zoom and map center
- Map Options: Map library defaults
- Basemap: Open Street Map
- Layers: WMS service from GeoMet with 1 Radar layer
- Layer Name: RADAR_1KM_RRAI
- Add attribution
- Specify WMS version 1.3.0
Although we are using many JavaScirpt libraries, this isn’t meant to be a comparison/competition between them. Way too much to unpack there, and not the purpose of this article. In some ways, this article shows how it doesn’t matter which library you use - WMS data can be leveraged by all of these libraries. WMS is handled a bit differently, but these differences are minor - many bigger application factors will determine the library that works for you.
Leaflet
Leaflet is a very popular open-source JavaScript library for building interactive maps. It has a very simple and effective WMS implementation. It doesn’t natively support the GetCapabilities request - but that isn’t required for our examples. If you know what you want (map layers from the service), just set it up, and Leaflet will take care of the rest.
// Add a wms service and specify the layer(s) of interest
var wmsLayer = L.tileLayer.wms('https://geo.weather.gc.ca/geomet?', {
layers: 'RADAR_1KM_RRAI'
}).addTo(map);
Beyond the basic layers parameter, we can specify a few additional parameters to handle transparency, preferred image format, and attribution. If you investigate the GetCapabilities request manually, you can find the parameters and values that are available for this service.
// Add the GeoMet WMS (Radar layer) with some additional parameters
var wmsLayer = L.tileLayer.wms('https://geo.weather.gc.ca/geomet?', {
layers: 'RADAR_1KM_RRAI', //missing comma
version: '1.3.0',
transparent: true,
format: 'image/png',
attribution: '<a href="https://eccc-msc.github.io/open-data/licence/readme_en/">ECCC</a>'
}).addTo(map);
Now, all we need to do is create the basic HTML page, the Leaflet JavaScript library source code and our WMS code. Based on our general requirements, we will add a basemap.
ArcGIS JavaScript API (Multiple versions)
Esri’s ArcGIS API for JavaScript has been around for a long time. Version 3x is now considered Esri’s ”legacy” JavaScript API with no new feature development underway. The good news, it is a very mature library with some features still not available in the 4x replacement. ArcGIS JavaScript API 4x is at version 4.19 at the time of writing and has almost reached parallel functionality - and moved beyond 3x in many areas (2D/3D views). Both versions use an Asynchronous Module Definition (AMD) format - this was to ”make code easier to author and debug” (DojoToolkit) but it does come with its learning curve. This adds some additional lines of code for simple apps - with the benefit of a large functionality matrix and feature-rich modules for bigger applications that go beyond mapping and deeper into the world of GIS.
ArcGIS JavaScript API 3x
Unlike Leafet, the ArcGIS JavaScript API 3x default behaviour is to execute a GetCapabilities request when a WMS Layer is added to the map. Unfortunately, this might lead to some CORS issues - although you can use a simple proxy if you get stuck. To bypass the automatic GetCapabilities request (and possible CORS issue), we can create a resourceInfo object and include it with the WMSLayer. Much like Leaflet, by including the resourceInfo data, a getMap request is made directly.
// resourceInfo object to describe the WMS layers (and bypass the GetCapabilities request)
var resourceInfo = {
extent: new Extent(-126.40869140625, 31.025390625, -109.66552734375, 41.5283203125, {
wkid: 3857
}),
description: "Radar",
layerInfos: [layer1],
layers: "RADAR\_1KM\_RRAI",
version: "1.3.0"
};
// Now we can add the WMS layer details with our resourceInfo object
// in the wmsLayer constructor
var wmsLayer = new WMSLayer('https://geo.weather.gc.ca/geomet/?lang=en&service=WMS', {
resourceInfo: resourceInfo,
copyright: 'ECCC',
visibleLayers: ['RADAR_1KM_RRAI']
});
map.addLayers([wmsLayer]);
Same with the last example, we build the remaining HTML page and add our basemap.
ArcGIS JavaScript API 4x
Similar to 3x, the ArcGIS JavaScript API 4x default behaviour is to execute a GetCapabilities request when a WMS Layer is added to the map. Missing in the new version is a way to work around this. So currently there is no resourceInfo object and we can’t bypass the getCapabilities request.
“The WMSLayer is used to create layers based on OGC Web Map Services (WMS). The WMSLayer initially executes a WMS GetCapabilities request, which might require CORS or a proxy page.” ~ Esri WMSLayer reference
// Create the WMS layer and specify sublayers
var wmsLayer = new WMSLayer('https://geo.weather.gc.ca/geomet/?lang=en&service=WMS', {
sublayers: [
{
name: "RADAR_1KM_RRAI",
title: "RRAI Radar",
description: "Radar"
}
],
copyright: '<a href="https://eccc-msc.github.io/open-data/licence/readme_en/">ECCC</a>',
description: "radarWMS",
title: "Radar PN",
version: "1.3.0"
});
OpenLayers
The OpenLayers map library continues to be a free, Open Source JavaScript option that doesn’t get much press but continues to have a strong user base. For some reason, it gets compared to Leaflet and mislabelled as “more complicated”. True in some ways, but it really will depend on what you are trying to accomplish. OpenLayers has more in common with the ArcGIS APIs: It crosses over the web-map to web-GIS threshold in similar ways: Feature-rich and able to do more complicated tasks with ease.
Back to OpenLayers… to add WMS layers we use the ImageWMS image source to connect to the GeoMet service. In the config, we add a params object with the list of layers (this is mandatory). The version defaults to 1.3, but added her to posterity, along with our attribution.
// Add the Image layer with WMS source
new ol.layer.Image({
source: new ol.source.ImageWMS({
url: 'https://geo.weather.gc.ca/geomet/',
params: {
LAYERS: 'RADAR_1KM_RRAI',
version: '1.3.0'
},
attributions: [
'Radar <a href="https://eccc-msc.github.io/open-data/licence/readme_en/">ECCC</a>', ol.source.OSM.ATTRIBUTION
]
})
});
Map Libraries and WMS
Regardless of the mapping library, the GeoMet service has a vast amount of data available to enrich your app. Don’t go by the amount of code, as this can be misleading. Easy things might take more lines of code, but complex items might take less, or not available in all libraries. Also not saying my approach is crafted to perfection.
One thing to note, since the GeoMet WMS has many layers, the service is not small. The impact of running a GetCapabilities request is noticeable and may impact map load performance. If you can avoid this extra request, do so. Other WMS servers may also have CORS issues with a proxy required for GetCapabilities. Two good reasons if you can avoid it and you know the service isn’t going to change.
The GeoMet WMS works great in all libraries, which is a real benefit of WMS and its open/standard protocol. WMS also has time-aware layer information if you want to take your radar application to the next level - play the last 3 hours of radar data in a simulation, or specify from one of a few symbology settings, etc.
Helpful Links
- MSC-GeoMet Service readme
- Leaflet JavaScript library
- ArcGIS JavaScript API 3x
- ArcGIS JavaScript API 4x
- OpenLayers JavaScript library
Cover image: CC-BY Dirk Breuel (Flickr) May 07, 2021. Modified from original.
If you found my writing entertaining or useful and want to say thanks, you can always buy me a coffee.