EMODnetWFS: Access EMODnet Web Feature Service data through R | Emodnet Biology

EMODnetWFS: Access EMODnet Web Feature Service data through R

Introduction

The package was designed to make EMODnet vector data layers easily accessible in R. The package allows users to query information on and download data from all available EMODnet Web Feature Service (WFS) endpoints directly into their R working environment. Data are managed as sf objects, which are currently the state-of-the-art in handling of vector spatial data in R. The package also allows user to specify the coordinate reference system of imported data.

 

Data Product

Installation

You can install the development version of EMODnetWFS from GitHub with:

remotes::install_github("EMODnet/EMODnetWFS", build_vignettes = TRUE)
 

Explore the EMODnet WFS services with R

For this tutorial we will make use of the sf, dplyr and mapview packages. The simple features sf package is a well known standard for dealing with geospatial vector data. The package dplyr is a strong library for data manipulation. This package also loads magickr’s pipe operator %>%, which allows to write pipelines in R. To visualize geometries, mapview will create quick interactive maps.

Run this line to install these packages:

install.packages(c("sf", "dplyr", "mapview"))

 

With the EMODnetWFS package, we can explore and combine the data served by the EMODnet lots through OGC Web Feature Services or WFS.

Imagine we are interested on seabed substrates. The first stop is to choose what EMODnet lot can provide with these data. For that, we can check the services available on the emodnet_wfs dataset contained inside the package.

library(EMODnetWFS)
library(mapview)
library(dplyr)
library(sf)

emodnet_wfs
#> # A tibble: 15 x 2
#>    service_name                          service_url                            
#>    <chr>                                 <chr>                                  
#>  1 bathymetry                            https://ows.emodnet-bathymetry.eu/wfs  
#>  2 biology                               http://geo.vliz.be/geoserver/Emodnetbi~
#>  3 biology_occurrence_data               http://geo.vliz.be/geoserver/Dataporta~
#>  4 chemistry_marine_litter               https://www.ifremer.fr/services/wfs/em~
#>  5 chemistry_time_series_location        http://emodnet02.cineca.it/geoserver/w~
#>  6 geology_sea_floor_bedrock             https://drive.emodnet-geology.eu/geose~
#>  7 geology_marine_minerals               https://drive.emodnet-geology.eu/geose~
#>  8 geology_seabed_substrate_maps         https://drive.emodnet-geology.eu/geose~
#>  9 geology_events_and_probabilities      https://drive.emodnet-geology.eu/geose~
#> 10 geology_coastal_behavior              https://drive.emodnet-geology.eu/geose~
#> 11 geology_submerged_landscapes          https://drive.emodnet-geology.eu/geose~
#> 12 human_activities                      https://ows.emodnet-humanactivities.eu~
#> 13 physics                               https://geoserver.emodnet-physics.eu/g~
#> 14 seabed_habitats_general_datasets_and~ https://ows.emodnet-seabedhabitats.eu/~
#> 15 seabed_habitats_individual_habitat_m~ https://ows.emodnet-seabedhabitats.eu/~

 

The column service_name shows services available, while service_url has the corresponding base url to perform a WFS request. The Seabed portal should have the data we are looking for. A WFS client can be created by passing the corresponding service_name to the function emodnet_init_wfs_client(). The layers available to this WFS client are consulted with emodnet_get_wfs_info().

seabed_wfs_client <- emodnet_init_wfs_client(service = "seabed_habitats_general_datasets_and_products")
#> Loading ISO 19139 XML schemas...
#> Loading ISO 19115 codelists...
#> √ WFS client created succesfully
#> i Service: 'https://ows.emodnet-seabedhabitats.eu/emodnet_open/wfs'
#> i Version: '2.0.0'

emodnet_get_wfs_info(wfs = seabed_wfs_client)
#> # A tibble: 21 x 9
#>    data_source service_name service_url layer_namespace layer_name title
#>    <chr>       <chr>        <chr>       <chr>           <chr>      <chr>
#>  1 emodnet_wfs seabed_habi~ https://ow~ emodnet_open    art17_hab~ 2013~
#>  2 emodnet_wfs seabed_habi~ https://ow~ emodnet_open    art17_hab~ 2013~
#>  3 emodnet_wfs seabed_habi~ https://ow~ emodnet_open    art17_hab~ 2013~
#>  4 emodnet_wfs seabed_habi~ https://ow~ emodnet_open    art17_hab~ 2013~
#>  5 emodnet_wfs seabed_habi~ https://ow~ emodnet_open    art17_hab~ 2013~
#>  6 emodnet_wfs seabed_habi~ https://ow~ emodnet_open    art17_hab~ 2013~
#>  7 emodnet_wfs seabed_habi~ https://ow~ emodnet_open    art17_hab~ 2013~
#>  8 emodnet_wfs seabed_habi~ https://ow~ emodnet_open    art17_hab~ 2013~
#>  9 emodnet_wfs seabed_habi~ https://ow~ emodnet_open    habitat_p~ Coll~
#> 10 emodnet_wfs seabed_habi~ https://ow~ emodnet_open    habitat_p~ Coll~
#> # ... with 11 more rows, and 3 more variables: abstract <chr>, class <chr>,
#> #   format <chr>

 

Each layer is explained in the abstract column. We can see several layers with the information provided by the EU member states for the Habitats Directive 92/43/EEC reporting. We will select the layers about coastal lagoons, mudflats and sandbanks with their respective layer_name.

habitats_directive_layer_names <- c("art17_hab_1110", "art17_hab_1140", "art17_hab_1150")

emodnet_get_layer_info(wfs = seabed_wfs_client, layers = habitats_directive_layer_names)
#> # A tibble: 3 x 9
#>   data_source service_name service_url layer_namespace layer_name title abstract
#>   <chr>       <chr>        <chr>       <chr>           <chr>      <chr> <chr>   
#> 1 emodnet_wfs https://ows~ seabed_hab~ emodnet_open    art17_hab~ 2013~ "Gridde~
#> 2 emodnet_wfs https://ows~ seabed_hab~ emodnet_open    art17_hab~ 2013~ "Gridde~
#> 3 emodnet_wfs https://ows~ seabed_hab~ emodnet_open    art17_hab~ 2013~ "Gridde~
#> # ... with 2 more variables: class <chr>, format <chr>

 

We are now ready to read the layers into R with emodnet_get_layers(). EMODnetWFS reads the geometries as simple features (See sf package) transformed to 4326 by default. Specifying another map projection is possible by passing a EPGS code or projection string with emodnet_get_layers(crs = "your projection"). The argument reduce_layers = TRUE stack all the layers in one single tibble. Default is FALSE and returns a list of sf objects, one per layer.

habitats_directive_layers <- emodnet_get_layers(wfs = seabed_wfs_client, 
                                                layers = habitats_directive_layer_names, 
                                                reduce_layers = TRUE)  
#> i crs transformed from 3035 to 4326

class(habitats_directive_layers)
#> [1] "sf"         "data.frame"

glimpse(habitats_directive_layers)
#> Rows: 221
#> Columns: 9
#> $ gml_id              <chr> "art17_hab_1110.13", "art17_hab_1110.22", "art1...
#> $ habitat_code        <chr> "1110", "1110", "1110", "1110", "1110", "1110",...
#> $ ms                  <chr> "DK", "ES", "ES", "PT", "PT", "PL", "DK", "FR",...
#> $ region              <chr> "ATL", "MAC", "MMAC", "MMAC", "MATL", "MBAL", "...
#> $ cs_ms               <chr> "U2+", "U1+", "U1+", "XX", "U1-", "U1-", "U1-",...
#> $ country_code        <chr> "Denmark", "Spain", "Spain", "Portugal", "Portu...
#> $ habitat_code_uri    <chr> "http://dd.eionet.europa.eu/vocabulary/art17_20...
#> $ habitat_description <chr> "Sandbanks which are slightly covered by sea wa...
#> $ geom                <MULTISURFACE [°]> MULTISURFACE (POLYGON ((8.0..., MU...

 

Run the following code to have a quick look at the layers geometries

# Transform to Polygon geometry type from Multisurface
if(unique(st_geometry_type(habitats_directive_layers)) == "MULTISURFACE"){
  habitats_directive_layers <- habitats_directive_layers %>% 
    st_cast(to = "GEOMETRYCOLLECTION") %>% 
    st_collection_extract(type = "POLYGON")
}

# Visualize
map <- mapview(habitats_directive_layers, zcol = "habitat_description", burst = TRUE)

map

 

Furthermore, we can get data from other EMODnet lots and combine them. The Human Activities portal provides the maritime boundaries of the European Union state members. This time we will not initiate a WFS client, but we will call the service directly. The WFS client will be generated on the fly.

Same as before, we have a look at the layers available first.

emodnet_get_wfs_info(service = "human_activities")
#> √ WFS client created succesfully
#> i Service: 'https://ows.emodnet-humanactivities.eu/wfs'
#> i Version: '2.0.0'
#> # A tibble: 82 x 9
#>    data_source service_name service_url layer_namespace layer_name title
#>    <chr>       <chr>        <chr>       <chr>           <chr>      <chr>
#>  1 emodnet_wfs human_activ~ https://ow~ emodnet         activelic~ Acti~
#>  2 emodnet_wfs human_activ~ https://ow~ emodnet         aquacultu~ Advi~
#>  3 emodnet_wfs human_activ~ https://ow~ emodnet         baltic     Advi~
#>  4 emodnet_wfs human_activ~ https://ow~ emodnet         blacksea   Advi~
#>  5 emodnet_wfs human_activ~ https://ow~ emodnet         longdista~ Advi~
#>  6 emodnet_wfs human_activ~ https://ow~ emodnet         market     Advi~
#>  7 emodnet_wfs human_activ~ https://ow~ emodnet         mediterra~ Advi~
#>  8 emodnet_wfs human_activ~ https://ow~ emodnet         northsea   Advi~
#>  9 emodnet_wfs human_activ~ https://ow~ emodnet         northwest~ Advi~
#> 10 emodnet_wfs human_activ~ https://ow~ emodnet         outermost~ Advi~
#> # ... with 72 more rows, and 3 more variables: abstract <chr>, class <chr>,
#> #   format <chr>

 

The layer_name for the maritime boundaries seems to be maritimebnds. This dataset was developed based on the official data provided by the European Environmental Agency and the Maritime Boundaries Database compiled by MarineRegions.org (Flanders Marine Institute, 2019).

maritime_boundaries <- emodnet_get_layers(service = "human_activities", 
                                          layers = "maritimebnds", 
                                          reduce_layers = TRUE)
#> √ WFS client created succesfully
#> i Service: 'https://ows.emodnet-humanactivities.eu/wfs'
#> i Version: '2.0.0'

glimpse(maritime_boundaries)
#> Rows: 198
#> Columns: 11
#> $ gml_id     <chr> "maritimebnds.1", "maritimebnds.2", "maritimebnds.3", "m...
#> $ mblszotpid <chr> "4", "3", "4", "1", "0", "0", "1", "0", "5", "1", "0", "...
#> $ localid    <chr> "63", "86", "60", "1024", "1022", "200", "1023", "1021",...
#> $ sitename   <chr> "Continental Shelf", "Delimitation line between states",...
#> $ legalfound <date> 1989-02-10, 1989-06-29, 1972-09-28, 1990-01-01, 1990-01...
#> $ legalfou_1 <chr> "SWE_POL_1989_CS.pdf", "SWE_POL_RUS_1989_MB.pdf", "SWE_F...
#> $ country    <chr> "Sweden, Poland", "Sweden, Poland, Russia", "Sweden, Fin...
#> $ nationalle <chr> "Bilateral", "Multilateral", "Bilateral", "Unilateral", ...
#> $ nutscode   <chr> "SE, PL", "SE, PL, RU", "SE, FI", "LV", "LV", "DE", "LT"...
#> $ mblsds_mbl <chr> "Agreement concerning the Delimitation of the Continenta...
#> $ the_geom   <MULTILINESTRING [°]> MULTILINESTRING ((16.51089 ..., MULTILIN...

 

The sitename variable shows the different types of boundaries. For illustration purposes, we will filter the Territorial Seas.

maritime_boundaries <- maritime_boundaries %>% filter(sitename == "Territory sea (12 nm)")

glimpse(maritime_boundaries)
#> Rows: 30
#> Columns: 11
#> $ gml_id     <chr> "maritimebnds.4", "maritimebnds.6", "maritimebnds.9", "m...
#> $ mblszotpid <chr> "1", "1", "1", "4", "1", "1", "1", "1", "1", "1", "1", "...
#> $ localid    <chr> "1024", "1023", "1000", "78", "119", "1001", "25", "1009...
#> $ sitename   <chr> "Territory sea (12 nm)", "Territory sea (12 nm)", "Terri...
#> $ legalfound <date> 1990-01-01, 1992-06-24, 1936-09-16, 1987-09-06, 1985-01...
#> $ legalfou_1 <chr> "LVA_1990_Law", "LTU_1992_Legislation.pdf", "GRC_1936_la...
#> $ country    <chr> "Latvia", "Lithuania", "Greece", "France, Dominica", "Th...
#> $ nationalle <chr> "Unilateral", "Unilateral", "Unilateral", "Bilateral", "...
#> $ nutscode   <chr> "LV", "LT", "GR", "FR, DO", "NL", "MN", "NL, BE", "TR", ...
#> $ mblsds_mbl <chr> "\"Law of the Republic of Latvia \"\"On the Border of th...
#> $ the_geom   <MULTILINESTRING [°]> MULTILINESTRING ((23.27798 ..., MULTILIN...

 

Add the maritime boundaries to the previous map with this line.

map + mapview(maritime_boundaries)

 

We have now combined data from the Seabed Habitats and Human Activities portals. However, there is more! EMODnet provides also physics, chemistry, biological or bathymetry data. Explore all the layers available with.

emodnet_get_all_wfs_info()

 

Advanced use

There is more that can accomplished by using the EMODnet WFS services than downloading data. The EMODnetWFS package is built on top of the ows4R library, meaning that all the functionalities of this package are available for EMODnetWFS. The ows4R returns a special type of R object called R6. You can learn more in Hadley Wickham’s chapter on R6 Objects of the Advance R book.

For instance: it is not efficient to read a large dataset into R just and later subset part of it. This requires longer waiting times and morebandwidth usage, and in very large datasets it would simply not be possible. For instance, all the occurrences data available through the EMODnet Biology portal are stored in one table: These are approximately 30 millions rows! In this case, we suggest you access the EMODnet Biology occurrence data through the download toolbox or the eurobis R package instead.

# Won't work
emodnet_get_layers(service = "biology_occurrence_data", layers = "eurobis-obisenv")

 

However, by using WFS services you can request some analysis to be performed in the EMODnet servers. For example: using CQL filters, you can send a query that will occur on the server side. Only the information you need will be send back.

Rethinking the case before where we downloaded all the european maritime boundaries and later we filtered only the Territorial Seas, we could have retrieved directly the Territorial Seas.

First, we start a new WFS client with emodnet_init_wfs_client

human_activities <- emodnet_init_wfs_client(service = "human_activities")
#> √ WFS client created succesfully
#> i Service: 'https://ows.emodnet-humanactivities.eu/wfs'
#> i Version: '2.0.0'

 

We can use now getCapabilities to see the inherited methods of the WFS client.

human_activities_caps <- human_activities$getCapabilities()

human_activities_caps
#> <WFSCapabilities>
#>   Inherits from: <OWSCapabilities>
#>   Public:
#>     attrs: list
#>     clone: function (deep = FALSE) 
#>     defaults: list
#>     encode: function (addNS = TRUE, geometa_validate = TRUE, geometa_inspire = FALSE) 
#>     ERROR: function (text) 
#>     findFeatureTypeByName: function (expr, exact = TRUE) 
#>     getClass: function () 
#>     getClassName: function () 
#>     getFeatureTypes: function (pretty = FALSE) 
#>     getOperationsMetadata: function () 
#>     getOWSVersion: function () 
#>     getRequest: function () 
#>     getService: function () 
#>     getServiceIdentification: function () 
#>     getServiceProvider: function () 
#>     getServiceVersion: function () 
#>     getUrl: function () 
#>     INFO: function (text) 
#>     initialize: function (url, version, logger = NULL) 
#>     logger: function (type, text) 
#>     loggerType: NULL
#>     verbose.debug: FALSE
#>     verbose.info: FALSE
#>     WARN: function (text) 
#>     wrap: FALSE
#>   Private:
#>     featureTypes: list
#>     fetchFeatureTypes: function (xmlObj, version) 
#>     operationsMetadata: OWSOperationsMetadata, R6
#>     owsVersion: 1.1
#>     request: OWSGetCapabilities, OWSRequest, OGCAbstractObject, R6
#>     service: WFS
#>     serviceIdentification: OWSServiceIdentification, R6
#>     serviceProvider: OWSServiceProvider, R6
#>     serviceVersion: 2.0.0
#>     system_fields: verbose.info verbose.debug loggerType wrap attrs defaults
#>     url: https://ows.emodnet-humanactivities.eu/wfs
#>     xmlElement: NULL
#>     xmlNamespace: NULL
#>     xmlNodeToCharacter: function (x, ..., indent = "", tagSeparator = "\n")

 

Select now the maritime boundaries feature using the findFeatureTypeByName method.

human_activities_caps_layer <- human_activities_caps$findFeatureTypeByName("emodnet:maritimebnds")

human_activities_caps_layer
#> <WFSFeatureType>
#>   Inherits from: <OGCAbstractObject>
#>   Public:
#>     attrs: list
#>     clone: function (deep = FALSE) 
#>     defaults: list
#>     description: NULL
#>     encode: function (addNS = TRUE, geometa_validate = TRUE, geometa_inspire = FALSE) 
#>     ERROR: function (text) 
#>     features: NULL
#>     getAbstract: function () 
#>     getBoundingBox: function () 
#>     getClass: function () 
#>     getClassName: function () 
#>     getDefaultCRS: function () 
#>     getDescription: function (pretty = FALSE) 
#>     getFeatures: function (...) 
#>     getKeywords: function () 
#>     getName: function () 
#>     getTitle: function () 
#>     INFO: function (text) 
#>     initialize: function (xmlObj, capabilities, version, logger = NULL) 
#>     logger: function (type, text) 
#>     loggerType: NULL
#>     verbose.debug: FALSE
#>     verbose.info: FALSE
#>     WARN: function (text) 
#>     wrap: FALSE
#>   Private:
#>     abstract: These conventions list the coordinates of points which a ...
#>     capabilities: WFSCapabilities, OWSCapabilities, OGCAbstractObject, R6
#>     defaultCRS: crs
#>     fetchFeatureType: function (xmlObj, version) 
#>     gmlIdAttributeName: gml_id
#>     keywords: features maritimebnds
#>     name: emodnet:maritimebnds
#>     system_fields: verbose.info verbose.debug loggerType wrap attrs defaults
#>     title: Maritime Boundaries
#>     url: https://ows.emodnet-humanactivities.eu/wfs
#>     version: 2.0.0
#>     WGS84BoundingBox: -73.2688140869141 4.5029821395874 35.9677352905273 76.06 ...
#>     xmlElement: NULL
#>     xmlNamespace: NULL
#>     xmlNodeToCharacter: function (x, ..., indent = "", tagSeparator = "\n")

 

And finally, we write a CQL filter that can be passed to the getFeatures method.

cql_filter = URLencode("sitename='Territory sea (12 nm)'")

maritime_boundaries_filtered <- human_activities_caps_layer$getFeatures(cql_filter = cql_filter)

glimpse(maritime_boundaries_filtered)
#> Rows: 30
#> Columns: 11
#> $ gml_id     <chr> "maritimebnds.4", "maritimebnds.6", "maritimebnds.9", "m...
#> $ mblszotpid <chr> "1", "1", "1", "4", "1", "1", "1", "1", "1", "1", "1", "...
#> $ localid    <chr> "1024", "1023", "1000", "78", "119", "1001", "25", "1009...
#> $ sitename   <chr> "Territory sea (12 nm)", "Territory sea (12 nm)", "Terri...
#> $ legalfound <date> 1990-01-01, 1992-06-24, 1936-09-16, 1987-09-06, 1985-01...
#> $ legalfou_1 <chr> "LVA_1990_Law", "LTU_1992_Legislation.pdf", "GRC_1936_la...
#> $ country    <chr> "Latvia", "Lithuania", "Greece", "France, Dominica", "Th...
#> $ nationalle <chr> "Unilateral", "Unilateral", "Unilateral", "Bilateral", "...
#> $ nutscode   <chr> "LV", "LT", "GR", "FR, DO", "NL", "MN", "NL, BE", "TR", ...
#> $ mblsds_mbl <chr> "\"Law of the Republic of Latvia \"\"On the Border of th...
#> $ the_geom   <MULTILINESTRING> MULTILINESTRING ((23.27798 ..., MULTILINESTR...

 

More information

References

Blondel, Emmanuel. (2020, May 27). ows4R: R Interface to OGC Web-Services (Version 0.1-5). Zenodo. http://doi.org/10.5281/zenodo.3860330

Flanders Marine Institute (2019). Maritime Boundaries Geodatabase, version 11. Available online at https://www.marineregions.org/. https://doi.org/10.14284/382.

Hadley Wickham, Romain François, Lionel Henry and Kirill Müller (2020). dplyr: A Grammar of Data Manipulation. R package version 1.0.2.https://CRAN.R-project.org/package=dplyr

Pebesma E (2018). “Simple Features for R: Standardized Support for Spatial Vector Data.” The R Journal, 10(1), 439–446. doi: 10.32614/RJ-2018-009, https://doi.org/10.32614/RJ-2018-009.

R Core Team (2020). R: A language and environment for statistical computing. R Foundation for Statistical Computing, Vienna, Austria. URL https://www.R-project.org/.

Tim Appelhans, Florian Detsch, Christoph Reudenbach and Stefan Woellauer (2020). mapview: Interactive Viewing of Spatial Data in R. R package version 2.9.0. https://CRAN.R-project.org/package=mapview

 

Code

Please cite this package as:

Anna Krystalli (2020). EMODnetWFS: Access EMODnet Web Feature Service data through R. R package version 0.0.2. https://github.com/EMODnet/EMODnetWFS. Integrated data products created under the European Marine Observation Data Network (EMODnet) Biology project (EASME/EMFF/2017/1.3.1.2/02/SI2.789013), funded by the by the European Union under Regulation (EU) No 508/2014 of the European Parliament and of the Council of 15 May 2014 on the European Maritime and Fisheries Fund.

 

FOLLOW US

Website hosted and developed by VLIZ

Copyright 2017 - EMODnet - THE EUROPEAN MARINE OBSERVATION AND DATA NETWORK