Working with WMS rasters

library(Argentum)
library(terra)
library(sf)

Why WMS at all

If WFS gives you real geometries, why ever ask for a picture?

Three reasons, and they come up constantly with Argentine data.

Some layers only exist as WMS. Satellite imagery, orthophotos, scanned historical charts and relief shading have no vector equivalent. The publisher has a raster; WMS is how they share it.

Some WFS layers are too heavy to be worth it. A national parcel layer is millions of polygons. If you only need it as a visual backdrop, downloading the geometry is wasteful — ask the server to draw it instead.

The publisher’s own cartography carries information. A cadastral server renders parcels with the symbology its own staff use. Reproducing that from raw geometry is work you do not need to do.

The one required argument

WMS has no concept of “the whole layer”. Every request renders a fixed rectangle, so bbox is mandatory:

ign <- "https://wms.ign.gob.ar/geoserver/ows"

r <- argentum_read_wms(
  ign,
  layer = "ign:provincia",
  bbox  = c(-59, -35, -57, -34),
  width = 1000
)

r

A note on the IGN basemap: the official capabaseargenmap layer now answers GetMap requests from generic clients with HTTP 403 (“Bloqueado: uso de WMS en mapa base”) - IGN reserves it for its own applications. The examples therefore use thematic layers, which remain open.

Only width is given above; height is derived from the aspect ratio of the bounding box, so the map is not stretched. Give both if you want a specific canvas, or give only height if that is what your layout constrains.

The bbox column of argentum_layers() gives you a starting extent when you do not know the layer’s footprint:

layers <- argentum_layers(ign, service = "wms")
layers[layers$name == "ign:provincia", "bbox"]

Combining raster and vector

This is the payoff. The raster comes back with its CRS set, so it composes directly with anything you read over WFS:

aoi <- c(-59, -35, -57, -34)

basemap  <- argentum_read_wms(ign, "ign:provincia", bbox = aoi, width = 1200)
boundaries <- argentum_read_wfs(ign, "ign:provincia", bbox = aoi, crs = 4326)

terra::plotRGB(basemap)
plot(sf::st_geometry(boundaries), add = TRUE, border = "white", lwd = 2)

Because argentum_read_wms() returns a plain SpatRaster, everything in terra works on it: crop(), mask(), project(), writeRaster().

Stacking layers in one request

Passing several layer names draws them in a single server-side composite, first at the bottom. One request instead of several, and the server handles the blending:

argentum_read_wms(
  ign,
  layer = c("ign:provincia", "ign:limite_politico"),
  bbox  = aoi
)

Legends

A rendered map without its legend is often unreadable. GetLegendGraphic returns the symbology the server used:

legend <- argentum_wms_legend(ign, "ign:provincia")
terra::plotRGB(legend)

The legend is an image, not a map, so it carries no CRS.

Choosing a format

"image/png" is the default and keeps transparency, which you need when overlaying. "image/jpeg" is meaningfully smaller for photographic imagery but cannot be transparent. "image/tiff" is worth requesting when you intend to analyse rather than display the pixels — though most servers still render to 8-bit RGB, so it is not a substitute for the underlying data.

argentum_read_wms(ign, "some_orthophoto", bbox = aoi, format = "image/jpeg",
                  transparent = FALSE)

A note on axis order

WMS 1.3.0 changed how coordinates are ordered: for CRSs whose authority definition is latitude-first, the BBOX parameter must be given latitude-first too. WMS 1.1.1 was always longitude-first.

This trips people up badly, because getting it wrong does not produce an error — it produces a map of the wrong place, or of the ocean.

Argentum handles the flip based on the version the server actually negotiated. It includes the Argentine Gauss-Krüger zones (EPSG:22171 through 22175), which are defined northing-first and are therefore affected, a case most generic clients get wrong.

You should never need to think about it. If you do hit a swapped map, force the older protocol and compare:

argentum_capabilities(ign, "wms", version = "1.1.1")