Skip to content

rOpenGov/rwfs

Repository files navigation

rwfs

rOG-badge Project Status: Active - The project has reached a stable, usable state and is being actively developed. R-CMD-check codecov

WFS client for R

Overview

This R package provides a client to access a Web Feature Service (WFS). Its core functionality is build on top of sf package.

Usage

Installation

rwfs can be installed from GitHub using devtools:

install.packages("devtools")
library("devtools")
devtools::install_github("ropengov/rwfs")

and loaded with

library(rwfs)

Request and client classes

The package consists of request and client classes which are of type R6. The request classes are used to construct a reference to a data source and provide access methods to the data. The client classes dispatch a request to obtain and possibly manipulate the data.

The request classes currently implemented are

  • WFSStreamingRequest for streaming data directly from a WFS,
  • WFSCachingRequest for downloading data from a WFS and caching it to disk and
  • GMLFile for reading data from a GML file.

All request classes are abstract with the exception of GMLFile meaning that the user of rwfs must provide a subclass for each relevant class and implement abstract methods in the classes. The user may provide additional methods for accessing the data for convenience.

The following client classes are currently implemented

  • WFSStreamingClient for streaming requests and
  • WFSCachingClient for downloading and caching requests and for local file access requests.

The client classes are available to be used directly. However, additional methods for manipulating data for user convenience can be provided by inheriting the classes.

In the following sections, we illustrate the use of the rwfs package by following two examples taken from the packages gisfin and fmi.

Inheritance of request classes

For streaming, the WFSStreamingRequest abstract class is required to be inherited to a subclass that implements the abstract method getDataSource(), which provides a data access reference. For example (taken from gisfin), the following class provides a URL to access data with the private method getURL(), which is called from getDataSource():

GeoStatFiWFSRequest <- R6::R6Class(
  "GeoStatFiWFSRequest",
  inherit = rwfs::WFSStreamingRequest,
  private = list(
    getURL = function() {
      url <- paste0("http://geo.stat.fi/geoserver/", private$getPathString(), "/wfs?", private$getParametersString())
      return(url)
    }
  ),
  public = list(
    getDataSource = function() private$getURL(),
    
    getGeoStatFiLayers = function(path) {
      if (missing(path))
        stop("Required argument 'path' missing.")      
      return(self$setPath(path)$getCapabilities())
    },

    getGeoStatFiLayer = function(path, layer) {
      if (missing(path))
        stop("Required argument 'path' missing.")      
      if (missing(layer))
        stop("Required argument 'layer' missing.")
      return(self$setPath(path)$getFeature(typeName=layer))
    },
    
    getPopulationLayers = function() self$getGeoStatFiLayers("vaestoruutu"),
    getPopulation = function(layer) self$getGeoStatFiLayer("vaestoruutu", layer),
    getProductionAndIndustrialFacilitiesLayers = function() self$getGeoStatFiLayers("ttlaitokset/ttlaitokset:toimipaikat"),
    getProductionAndIndustrialFacilities = function(layer="ttlaitokset:toimipaikat") self$getGeoStatFiLayer("ttlaitokset/ttlaitokset:toimipaikat", layer),
    getEducationalInstitutionsLayers = function() self$getGeoStatFiLayers("oppilaitokset/oppilaitokset:oppilaitokset"),
    getEducationalInstitutions = function(layer="oppilaitokset:oppilaitokset") self$getGeoStatFiLayer("oppilaitokset/oppilaitokset:oppilaitokset", layer),
    getRoadAccidentsLayers = function() self$getGeoStatFiLayers("tieliikenne"),
    getRoadAccidents = function(layer) self$getGeoStatFiLayer("tieliikenne", layer),
    getPostalCodeAreaLayers = function() self$getGeoStatFiLayers("postialue"),
    getPostalCodeArea = function(layer) self$getGeoStatFiLayer("postialue", layer)
  )
)

The method getGeoStatFiLayers(path) lists available layers by setting path, which refers to the data set behind path in the service, and the method getGeoStatFiLayer(path, layers) which obtains the layer layer from the path data set. The rest of the methods are for convenience purpose for the user that cover the available data sets in the service.

Similar to WFSStreamingRequest, WFSCachingRequest must implement the getURL abstract method (example taken from fmi):

FMIWFSRequest <- R6::R6Class(
  "FMIWFSRequest",
  inherit = rwfs::WFSCachingRequest,
  private = list(
    apiKey = NA,
    
    getURL = function() {
      url <- paste0("http://data.fmi.fi/fmi-apikey/", private$apiKey, "/wfs?", private$getParametersString())
      return(url)
    }
  ),
  public = list(
    initialize = function(apiKey) {
      if (missing(apiKey))
        stop("Must specify the 'apiKey' parameter.")
      private$apiKey <- apiKey
    }    
  )
)

Here the class provides also a mechnism for storing an API key, which is required to access the service.

Inheritance of client classes

Continuing with the example from gisfin, there is no need to inherit WFSStreamingClient. However, for consistency the package provides the class GeoStatFiWFSClient inheriting WFSStreamingClient, which is exactly the same class but with a different name.

The FMIWFSClient class inheriting WFSCachingClient in the fmi example, sets the service access parameters and returns data after formatting it. For example, the getMonthlyWeatherRaster calls the setParameters() method in the request object and the getRaster() in the superclass to obtain a raster object.

FMIWFSClient <- R6::R6Class(
  "FMIWFSClient",
  inherit = rwfs::WFSCachingClient,
  private = list(
    processParameters = function(startDateTime=NULL, endDateTime=NULL, bbox=NULL, fmisid=NULL) {
      [...]
    },
    
    getRasterURL = function(parameters) {
      [...]
    }
  ),
  public = list(
    getDailyWeather = function(variables=c("rrday","snow","tday","tmin","tmax"), startDateTime, endDateTime, bbox=NULL, fmisid=NULL) {      
      [...]
    },
    
    getMonthlyWeatherRaster = function(startDateTime, endDateTime) {
      if (inherits(private$request, "FMIWFSRequest")) {
        if (missing(startDateTime) | missing(endDateTime))
          stop("Arguments 'startDateTime' and 'endDateTime' must be provided.")
        
        p <- private$processParameters(startDateTime=startDateTime, endDateTime=endDateTime)
        private$request$setParameters(request="getFeature",
                                      storedquery_id="fmi::observations::weather::monthly::grid",
                                      starttime=p$startDateTime,
                                      endtime=p$endDateTime)
      }
      
      response <- self$getRaster(parameters=list(splitListFields=TRUE))
      if (is.character(response)) return(character())
      NAvalue(response) <- 9999
      names(response) <- getRasterLayerNames(startDateTime=startDateTime,
                                             endDateTime=endDateTime,
                                             by="month",
                                             variables=c("MeanTemperature", "Precipitation"))
      return(response)
    }
  )
)

The getMonthlyWeatherRaster() method first checks that request object has been given and it is of the typeFMIWFSRequest. The method also does some data manipulation such as setting layer names for the raster object.

Accessing WFS

Once we have defined appropriate subclasses, we can build a request object and access WFS to obtain data with a client object. For examples, please look at the vignettes in the gisfin and in the fmi packages.

File access

An example to access a local GML file:

library(rwfs)
fileName <- tempfile()
download.file("http://geo.stat.fi/geoserver/vaestoalue/wfs?service=WFS&version=1.0.0&request=GetFeature&typeName=vaestoalue:suuralue_vaki2014", fileName)
request <- rwfs::GMLFile$new(fileName)
client <- rwfs::WFSCachingClient$new(request)
layer <- client$getLayer("suuralue_vaki2014")
print(layer@data)
plot(layer)
unlink(fileName)

Contact

You are welcome to:

Acknowledgements

Roger Bivand for helping with getting the WFS driver working on Windows.