SOS Operations

  collapse = TRUE,
  comment = "#>",
  concordance = TRUE

Supported Features

The package provides accessor functions for the supported parameters. It is recommended to access options from the lists returned by these functions instead of hard-coding them into scripts.

This section only lists the possibilities. Explanations follow in this document or can be found in the SOS specification.

SosSupportedOperations(version = "1.0.0")
SosSupportedOperations(version = "2.0.0")

The response format "text/csv" is not standard conform, but used by services as an alternative to XML encodings. The package readr must be installed to parse responses in this format. See the IOOS example in the services vignette (online only).


The output of the following calls are named lists (the name being the same as the value).


Default Options

Two kinds of default values can be found in (function calls in) sos4R: (i) default depending on other function parameters, and (ii) global defaults. Global defaults can be inspected (not changed!) using the following functions. If you want to use a different value please change the respective argument in function calls.


The process of data download also comprises (i) building requests, (ii) decoding responses, and (iii) applying the correct R data type to the respective data values. This mechanism is explained in detail in the vignette "Extending". The package comes with a set of predefined encoders, decoders and converters (output not shown here as it is very extensive).


Creating a SOS connection

The method SOS() is a construction method for classes encapsulating a connection to a SOS. It prints out a short statement when the connection was successfully established (i.e. the capabilities document was received) and returns an object of class SOS.

mySOS <- SOS(url = "", binding = "KVP")

To create a SOS connection you only need the URL of the service (i.e. the URL endpoint which can be used for HTTP requests). The service connection created above is used for all examples throughout this document.

All parameters except the service endpoint are optional and use default settings:

There are accessor methods for the slots, the "properties", of the class.


Print and summary methods are available for important classes, like SOS.


SOS Operations

sos4R implements the SOS core profile of version 1.0.0 comprising the operations GetCapabilities, DescribeSensor and GetObservation. This document focusses on the practical usage of the operations, so the reader is referred to the specification document for details.

The methods mirroring the SOS operations all contain debugging parameters inspect and verbose as described in the "Quickstart" vignette.


The GetCapabilities operations is automatically conducted during the connecting to a SOS instance. The response is the capabilities document, which contains a detailed description of the services capabilities. It's sections describe: service identification, service provider, operations metadata (parameter names, ...), filter capabilities, and contents (a list of offering descriptions). Please see section 8.2.3 of the SOS specification for details. If you want to inspect the original capabilities document XML, it can be re-requested using

sosCapabilitiesDocumentOriginal(sos = mySOS)

The actual operation can be started with the following function. It returns an object of class SosCapabilities which can be accessed later on by the function sosCaps() from an object of class SOS.

getCapabilities(sos = mySOS)

The parameters of the operation are:

Exploring the Capabilities Document

The respective parts of the capabilities document are modelled as R classes and can be accessed with accessor functions:


The first three functions extract clearly structured, self-explanatory parts of the document. You can use this to add proper accreditation to your figures, for example.

The contents part however is described in detail in section Metadata Extraction, as it can (and should) be used to extract query parameters.

The function sosTime() returns the time period for which observations are available within the service. To be precise, it accesses the ows:Range element of the parameter eventTime in the description of the GetObservation operation.


The operations supported by the SOS are listed in the ows:OperationsMetadata element, which is modelled as an R class, OwsOperationsMetadata, which contains a list of objects of class OwsOperation which in turn describe the allowed parameter values for calls to the operation. The operations metadata and individual operations can be inspected with the following functions.

sosOperation(mySOS, "GetCapabilities")
sosOperation(mySOS, sosGetCapabilitiesName)

The allowed response formats (the file format/encoding of the response), the response modes (for example inline or as attachment) and the result models (a qualified XML name of the root element of the response) differ for every operation of the service. The following accessor methods return either (i) a list (named by the operation names) of vectors (with the actual allowed parameter values), or (ii) with the unique parameter set to TRUE, a unique list of all allowed values. Please be aware that these are not allowed for all operations, not are all options supported by sos4R.


Some exemplary outputs of the operations are as follows (unnamed lists are simplified with toString()). Note the missing values for some operations (where options are not required they might not be available).

sosResponseMode(mySOS, unique = TRUE)

Spatial Reference Systems

For future analyses, but also for correct plotting, one must know the coordinate reference system (CRS) or spatial reference system (SRS) or the returned data. You can get this information using the method sosGetCRS() from various objects.

The function utilizes the EPSG code in GML attributes like srsName="urn:ogc:def:crs:EPSG:4326" to initialize an object of class CRS from the package sp. For SOS and SosObservationOffering objects these are taken from the bounding box given in the gml:boundedBy element.

# returns the CRS of offering(s) based on the CRS
# used in the element gml:boundedBy:

Mre examples for sosGetSRS() can be found in section Spatial Reference Systems.

Plotting SOS and Offerings

The content of the capabilities document allows the plotting of a service's offerings. The following example uses the packages maps, mapdata and maptools to create a background map. Plotting functions exist for objects of class SOS and SosObservationOffering, so offerings can also be plotted separately.

# background map:
library("maps"); library("mapdata"); library("maptools")
crs <- sosGetCRS(mySOS)[[1]]
worldHigh <- pruneMap(
        map(database = "worldHires",
            region = c("Germany"),
            plot = FALSE))
worldHigh.lines <- map2SpatialLines(worldHigh, proj4string = crs)

# the plot:
plot(worldHigh.lines, col = "grey50")
plot(mySOS, add = TRUE, lwd = 10, col = "red")
title(main = paste("Offerings by '", sosTitle(mySOS), "'", sep = ""),
        sub = toString(names(sosOfferings(mySOS))))


The DescribeSensor operation is specified in clause 8.3 of the SOS specification and its response is modeled in Sensor Model Language (SensorML) and Transducer Markup Language (TML) specifications.

The DescribeSensor operation is useful for obtaining detailed information of sensor characteristics encoded in either SensorML or TML. The sensor characteristics can include lists and definitions of observables supported by the sensor. [...]

The parameters of the operation are as follows. Please see other sections of this document on how to get supported respectively allowed values of request parameters from SOS metadata.

A simple example is as follows.

mySensor <- describeSensor(sos = mySOS,
        procedure = sosProcedures(mySOS)[[1]],
        outputFormat = 'text/xml; subtype="sensorML/1.0.1"', # space is needed!


All additional information presented in the following depends on compliance of the sensor description with the SensorML Profile for Discovery.

The coordinates data frame of a sensor description can be accessed with the common method sosCoordinates().


Other possibly useful parts of the sensor description can be accessed as well:


This includes the coordinates with unit and reference system information in the attributes of the returned object.

mySensor.coords <- sosCoordinates(mySensor)

The observed bounding box is also available for reuse.


The coordinates also allow the plotting of the sensor positions (see Figure below). Here it is assumed that the spatial reference system of the SOS is the same for data from the first offering and the sensor positions!

library("maps"); library("mapdata"); library("maptools")

# get sensor descriptions
procs <- unique(unlist(sosProcedures(mySOS)))
procs.descr <- lapply(X = procs, FUN = describeSensor,
                      sos = mySOS,
                      outputFormat = 'text/xml; subtype="sensorML/1.0.1"') <- sosGetCRS(procs.descr[[1]])
worldHigh <- pruneMap(map(database = "worldHires",
                          region = c("Germany"),
                          plot = FALSE))
worldHigh.lines <- map2SpatialLines(worldHigh, proj4string =

plot(worldHigh.lines, col = "grey50", ylim = c(44.0, 54.8))
for(x in procs.descr)
    plot(x, add = TRUE, pch = 19)
text(sosCoordinates(procs.descr)[c("x", "y")],
        labels = sosId(procs.descr), pos = 4, cex = 0.8)
title(main = paste("Sensors of", sosTitle(mySOS)))


The GetObservation operation is specified in clause 8.4 of the SOS specification. In this section, all matters around requesting data are explained - from extracting query parameters from metadata, and sending the request, till finally extracting data values and coordinates from the response.

A few utility functions exist to minimize a user's amount of work to create usual requests. They accept normal R types as input and return the respective class from sos4R with useful default settings. These function's names follow the pattern with sosCreate[name of object]() and exist for spatial and temporal filters.

Metadata Extraction for Request Building

It is recommended to extract the identifiers of procedures et cetera that are to be used for queries from the metadata description provided by the service, the capabilities document. This often ensures forward compatibility and minimizes typing errors. The offerings are the "index" of the service and therefore we concentrate on the contents section of the capabilities here.

The class SosContents simply contains a list of objects of the class SosObservationOffering which one can get directly from the connection object and also by name of the :


The output when printing the full list is quite extensive, so we concentrate on just on element of it in the following examples. Printing and summary methods are available of objects of the class SosObservationOffering.


The offerings list is named with the offering identifier, so the following statements return the same list.


The offering identifier is is used in the example below to extract the offering description of temperature measurements. The offerings list is a standard R list, so all subsetting operations are possible.

Note: The order of the offering list (as all other lists, e.g. procedures or observed properties) is not guaranteed to be the same upon every connection to a service. So indexing by name (though counteracting the mentioned forward compatibility, as names might change) is recommended at at least one point in the analysis so that changes in the contents of a service result in an error.

myOffering <- sosOfferings(mySOS)[["ws2500"]]

Metadata about the whole offering are identifier, name, and spatial and temporal extends.


The offerings also contains metadata about the format and model that are supported.


The spatial extend is given as a rectangular bounding box with two coordinates. The structure of the bounding box is kept flexible, as it simply returns a named list of lower and upper corner.


The optional attribute bbox can be used to obtain a bounding box matrix as used by package sp.

sosBoundedBy(myOffering, bbox = TRUE)

The temporal extent is modeled as an object of the respective class of the element in the offering description, which normally is a gml:TimePeriod, but does not have to be.


You can also access the actual timestamps with an accessor function sosTime().

myOffering.time <- sosTime(myOffering)

The structure of these elements is very flexible (with some of optional elements) and not self-explanatory. Therefore the parameter convert can be used to disable attempts to create R objects and return the raw parsed objects instead.

myOffering.time.unconverted <- sosTime(myOffering, convert = FALSE)

Furthermore the offering comprises lists of procedures, observed properties, and features of interest. In our example the feature and procedure identifiers are the same - this does not have to be the case.

Important Note: The order of these lists is not guaranteed to be the same upon every connection to a service.


All of the above can not only be requested for single offerings but also for complete SOS connections or for lists of offerings. The following examples only print out a part of the returned lists.


Also (parts of) a list of offerings are possible with these functions:


Please carefully inspect the structure in each case, as these functions will return named lists of lists and not combine procedures from different offerings. Consequently, some procedures could appear several times, but the association to the offering is still intact which is preferred at this stage.

Basic Request

getObservation(sos = mySOS, offering = myOffering, ...)

The mandatory attributes are sos, offering, observedProperty and responseFormat. The other parameters are set to NA and not used when building the request.

Please see section 8.4.2 of the SOS specification for details and other sections of this document for supported values respectively allowed values of request parameters. Note that different implementations might respond differently to missing parameters.

defaultResponseFormatGetObs <- gsub(pattern = "&quot;", replacement = "'", x = sosDefaultGetObsResponseFormat)

The returned data of all GetObservation operations is an XML document of type om:Observation, om:Measurement, or om:ObservationCollection which holds a list of the former two. All three of these have corresponding S4 classes, namely OmObservation, OmMeasurement, or OmObservationCollection.

The most straightforward (and most simple to use) methods to query certain observations are to request one (or several) specific observed property (phenomenon) or procedure (sensor). Note that the procedures and observed properties have to match the given offering, which the do not in the second case. In the next two cases, there is no temporal filter, so the code is not executed here because these request would potentially retrieve a lot of data, since there is no temporal (or thematical/spatial) limitation.

myObservationData.procedure.1 <- getObservation(sos = mySOS,
    offering = myOffering)
getObservation(sos = mySOS,
    offering = myOffering,
    procedure = sosProcedures(myOffering),
    #procedure = sosProcedures(myOffering)[[1]],
    observedProperty = as.list(names(sosObservedProperties(mySOS)[3:4])))

The following example requests data for about one day of temperature data and stores it in the object myObservationData. This feature is described extensively in section Temporal Filtering.

myObservationData <- getObservation(sos = mySOS,
        offering = myOffering,
        eventTime = sosCreateTime(sos = mySOS,
                                  time = "2018-01-01::2018-01-06"))

The logging output above starting with [sos4R] informs the user when the download of data is complete and when the parsing has finished. It even contains some information about the data, if possible. In following requests, this output is not included for brevity.

The response myObservationData of this request is the base for the next sections.

utils::str(myObservationData, max.level = 2)

Response Subsetting

Subsetting of elements in an OmObservationCollection can be done just like in a normal list (in fact, it just wraps at list of observations at this point), i.e. with the operators [ and [[. Summary functions are available for single observations or an observation collection.


The collection can also be subset in parts:


Observation collection indexing is possible with identifiers of procedure(s), observed property(ies), and feature(s) of interest.

index.foiId <- sosFeaturesOfInterest(myOffering)[[1]]
index.obsProp <- sosObservedProperties(myOffering)[[1]]
index.proc <- sosProcedures(myOffering)
index.proc.alternative1 <- sosProcedures(myOffering)[1]
index.proc.alternative2 <- sosProcedures(mySOS)


Result Extraction

Data Values can be extracted from observations, measurements and observation collections with the function sosResult(). The function returns an object of class data.frame. In the case of collections, it automatically binds the data frames (you can turn this off by adding bind = FALSE as a parameter).

myObservationData.result.2 <- sosResult(myObservationData[[1]])

Additional metadata, like units of measurement or definitions, is accessible via attributes() for every column of the data frame.


Spatial Information can be stored in an observation in several ways: (i) as a usual data attribute which is directly contained in the result data.frame, (ii) within a feature collection in the observation. In the latter case the utility functions sosCoordinates() and sosFeatureIds() can be used to extract the coordinates respectively the identifiers from OmObservationCollection or OmObservation classes. A variety of feature types gml:Point or sa:SamplingPoint are supported by sosCoordinates().


An observation collection also contains a bounding box of the contained observations, which can be extracted with the function sosBoundedBy(). The optional attribute bbox can be used to obtain a bounding box matrix as used by package sp.

sosBoundedBy(myObservationData, bbox = TRUE)

The combination of data values and coordinates strongly depends on the use case and existing spatial information.

The default column name for the feature identifiers is sosDefaultColumnNameFeatureIdentifier. If the name of the feature identifier attribute in the data table matches (which is the case for 52┬░North SOS), merge does not need additional information. In that case, the merging reduces to the following code: <- merge(x = myObservationData.result.2, y = myObservationData.coords)
utils::str(, max.level = 2)

And in that case, you can even save that step by specifying the attribute coordinates of the function sosResult which includes the merge of data values and coordinates as shown above.

head(sosResult(myObservationData[1], coordinates = TRUE))

Temporal Filtering

The possibly most typical temporal filter is a period of time for which measurements are of interest.

# temporal interval creation based on POSIXt classes:
lastWeek.period <- sosCreateTimePeriod(sos = mySOS,
    begin = (Sys.time() - 3600 * 24 * 7),
    end = Sys.time())

# when creating timestamps from strings note that `as.POSIXct`
# are in the locally configured timezone (see `Sys.timezone`)
period <- sosCreateTimePeriod(sos = mySOS,
        begin = as.POSIXct("2015/11/01"),
        end = as.POSIXct("2015/11/02"))
eventTime <- sosCreateEventTimeList(period)

Please note that the create function sosCreateEventTimeList() wraps the created objects in a list as required by the method getObservation().

The most comfortable creation function for event times is sosCreateTime(). It supports time intervals with starttime and endtime as character strings seperated by :: or / as defined by ISO 8601, section Time intervals}. The respective time stamps have to be parsable by parsedate::parse_iso_8601(..). If either one of the time stamps is missing, a GmlTimePosition wrapped in the appropriate relative temporal operator, e.g. "before".

sosCreateTime(sos = mySOS, time = "2007-07-07 07:00::2008-08-08 08:00")
sosCreateTime(sos = mySOS, time = "2007-07-07 07:00/2010-10-10 10:00")

sosCreateTime(sos = mySOS, time = "::2007-08-05")
sosCreateTime(sos = mySOS, time = "2007-08-05/")

Example: What was the minimum, average and maximum temperature during one week?

For temporal filtering, we use "POX"-based connection to the same SOS server:

mySOSpox <- SOS(url = "", 
             binding = "POX", useDCPs = FALSE)
nov2015 <- getObservation(sos = mySOSpox,
                          offering = myOffering,
                          eventTime = eventTime)
nov2015.result.1 <- sosResult(nov2015[[1]])

The default temporal operator is "during", but others are supported as well. The next example shows how to create a temporal filter for all observations taken after a certain point in time. Here the creation function creates just one object of class SosEventTime which must be added to a list manually before passing it to getObservation().

lastDay.instant <- sosCreateTimeInstant(
    time = as.POSIXct(Sys.time() - 3600 * 24), sos = mySOSpox)
lastDay.eventTime <- sosCreateEventTime(time = lastDay.instant,
    operator = SosSupportedTemporalOperators()[["TM_After"]])

Another example workflow, using a KVP-based connection.

sept15.period <- sosCreateTimePeriod(sos = mySOS,
                                     begin = parsedate::parse_iso_8601("2015-09-01 00:00"),
                                     end = parsedate::parse_iso_8601("2015-09-30 00:00"))
sept15.eventTimeList <- sosCreateEventTimeList(sept15.period)
obs.sept15 <- getObservation(sos = mySOS,
    offering = myOffering,
    eventTime = sept15.eventTimeList)

Spatial Filtering

The possibly most typical spatial filter is a bounding box within which measurements of interest must have been made. Here the creation function returns an object of class OgcBBOX, which can be wrapped in an object of class SosFeatureOfInterest, which is passed into the get-observation call.

request.bbox <- sosCreateBBOX(lowLat = 5.0, lowLon = 1.0,
                              uppLat = 10.0, uppLon = 3.0,
                              srsName = "urn:ogc:def:crs:EPSG::4326")
request.bbox.foi <- sosCreateFeatureOfInterest(spatialOps = request.bbox)

obs.sept15.bbox <- getObservation(sos = mySOSpox,
                                  offering = myOffering,
                                  featureOfInterest = request.bbox.foi,
                                  eventTime = sept15.eventTimeList)

Unfiltered versus spatially filtered coordinates of the responses (spatial filter did not match anything):


The returned object matches the structure of the coordinates table, but contains NA, and a warning is issued.

naCoords <- sosCoordinates(obs.sept15.bbox)

More advanced spatial filtering, for example based on arbitrary shapes et cetera, is currently not implemented. This could be implemented by implementing subclasses for GmlGeometry (including encoders) which must be wrapped in OgcBinarySpatialOp which extends OgcSpatialOps and can therefore be added to an object of class SosFeatureOfInterest as the spatial parameter.

Feature Filtering

The feature can not only be used for spatial filtering, but also to query specific FOIs. The following example extracts the identifiers from an offering and then creates an object of class SosFeatureOfInterest, which is passed into the get-observation call. Here the encoding function is called to show how the content of the result element will look like.

myOffering.fois <- sosFeaturesOfInterest(myOffering)
request.fois <- sosCreateFeatureOfInterest(
    objectIDs = list(myOffering.fois[[1]]))
encodeXML(obj = request.fois, sos = mySOSpox)

An exemplary GetObservation operation is as follows.

obs.oneWeek.fois <- getObservation(sos = mySOSpox,
    offering = myOffering,
    featureOfInterest = request.fois,
    eventTime = eventTime)

Value Filtering

Value Filtering is realized via the slot result in a GetObservation request. The filtering in the request is based on comparison operators and operands specified by OGC Filter Encoding (Vretanos, 2005).

The classes and methods of this specification are not yet implemented, but manual definition of the XML elements is possible with the methods of the package xml2.

The following code example uses a literal comparison of a property. The elements names are taken from constants within sos4R (with the naming scheme "<namespace><ElementName>Name"), but can equally as well be put in directly.

# TODO update result filter example with xml2
#filter.value <- -2.3
#filter.propertyname <- xmlNode(name = ogcPropertyNameName, namespace = ogcNamespacePrefix)
#xmlValue(filter.propertyname) <- "urn:ogc:def:property:OGC::Temperature"
#filter.literal <- xmlNode(name = ogcLiteralName, namespace = ogcNamespacePrefix)
#xmlValue(filter.literal) <- as.character(filter.value)
#filter.comparisonop <- xmlNode(name = ogcComparisonOpGreaterThanName,
#                               namespace = ogcNamespacePrefix,
#                               .children = list(filter.propertyname,
#                                                filter.literal))
#filter.result <- xmlNode(name = sosResultName,
#                         namespace = sosNamespacePrefix,
#                         .children = list(filter.comparisonop))

Please consult to the extensive documentation of the xml2 package for details. The commands above result in the following output which is inserted into the request without further processing.


Any object of class OgcComparisonOpsOrXMLOrNULL, which includes the class of the object returned by xmlNode(), i.e. XMLNode. These object can be used in the GetObservation request as the result parameter.

First, we request the unfiltered values for comparison, then again with the filter applied. The length of the returned results is compared in the end.

obs.oneWeek.filter <- getObservation(sos = mySOS,
        eventTime = eventTime,
        offering = sosOfferings(mySOS)[["wxt520"]],
        result = filter.result)
# request  values for the week with a value higher than 0 degrees:
obs.oneWeek.filter <- getObservation(sos = mySOS,
    eventTime = eventTime,
    offering = sosOfferings(mySOS)[["ATMOSPHERIC_TEMPERATURE"]],
    result = filter.result)

# print(paste("Filtered:", dim(sosResult(obs.oneWeek.filter))[[1]],
#   "-vs.- Unfiltered:", dim(sosResult(obs.oneWeek))[[1]]))

Result Exporting

A tighter integration with data structures of packages sp, spacetime, and sf (all on CRAN) is planned for the future. Please consult the developers for the current status.

As an example the following code creates a SpatialPointsDataFrame (can only contain one data value per position!) based on the features of a result.

obs.oneWeek <- getObservation(sos = mySOSpox,
    offering = myOffering,
    procedure = sosProcedures(myOffering),
    eventTime = eventTime)
# Create SpatialPointsDataFrame from result features
coords <- sosCoordinates(obs.oneWeek[[1]])
crs <- sosGetCRS(obs.oneWeek[[1]])
spdf <- SpatialPointsDataFrame(coords = coords[,1:2],
    data = data.frame(coords[,4]), proj4string = crs)

Spatial Reference Systems

For following analyses and plotting, the spatial reference system can be extracted with sosGetCRS(..).



The operation GetObservationById is defined in clause 10.1 of the SOS specification and not part of the core profile. But it is implemented as it is quite simple. The response is the same as described in the previous section. Optional parameters, and their defaults and supported values are the same as in GetObservation requests.

In this case the returned observation collection contains an om:Measurement element, which contains just one measured value and is parsed to an object of class OmMeasurement.

The result extraction works the same as with objects of class OmObservation.

obsId <- getObservationById(sos = mySOSpox, observationId = "")
sosResult(obsId, coordinates = TRUE)

Just as for getObservation() you can save the original response document with an automatically generated name or a selected one. It is saved into the current working directory and the name starts with the observation identifier. You can also read it back using the function sosParse().

Try the sos4R package in your browser

Any scripts or data that you put into this service are public.

sos4R documentation built on July 9, 2020, 5:07 p.m.