Author: Arnd Weber
Date: 2025-12-02

# hyd1d - Vignette

#### Arnd Weber

#### 2025-12-02

# Purpose

hyd1d is an R package that provides an S4-class and several functions to compute 1-dimensional water levels along the German federal waterways Elbe and Rhine.

# Use

## Installation

The package hyd1d is available from CRAN. To install it run:

    install.packages("hyd1d")

To install the recent developmental version from Github execute the following commands:

    install.packages("devtools")
    library(devtools)
    devtools::install_github("bafg-bund/hyd1d")

Afterwards hyd1d can be loaded like every other R package with the following command:

    library(hyd1d)

## S4-class WaterLevelDataFrame

All water level computations with hyd1d are based on the S4-class `WaterLevelDataFrame`. To compute water levels with one of the `waterLevel…()`-functions, a `WaterLevelDataFrame` has to be initialized with the homonymous `WaterLevelDataFrame()`-function:

    wldf <- WaterLevelDataFrame(river   = "Elbe",
                                time    = as.POSIXct("2016-12-21"),
                                station = seq(257, 262, 0.1))

The required information to initialize a `WaterLevelDataFrame` are the function arguments `river`, `time` and at least one of the two possible station arguments (`station` or `station_int`). With this information an object of class `WaterLevelDataFrame` can be created, which has the following structure:

    str(wldf)
    #> 'data.frame':    51 obs. of  3 variables:
    #> Formal class 'WaterLevelDataFrame' [package "hyd1d"] with 9 slots
    #>   ..@ .Data                   :List of 3
    #>   .. ..$ : num  257 257 257 257 257 ...
    #>   .. ..$ : int  257000 257100 257200 257300 257400 257500 257600 257700 257800 257900 ...
    #>   .. ..$ : num  NA NA NA NA NA NA NA NA NA NA ...
    #>   ..@ river                   : chr "Elbe"
    #>   ..@ time                    : POSIXct, format: "2016-12-21"
    #>   ..@ gauging_stations        :'data.frame': 0 obs. of  27 variables:
    #>   .. ..$ id                : int
    #>   .. ..$ gauging_station   : chr
    #>   .. ..$ uuid              : chr
    #>   .. ..$ km                : num
    #>   .. ..$ km_qps            : num
    #>   .. ..$ river             : chr
    #>   .. ..$ longitude         : num
    #>   .. ..$ latitude          : num
    #>   .. ..$ mw                : num
    #>   .. ..$ mw_timespan       : chr
    #>   .. ..$ pnp               : num
    #>   .. ..$ w                 : num
    #>   .. ..$ wl                : num
    #>   .. ..$ n_wls_below_w_do  : int
    #>   .. ..$ n_wls_above_w_do  : int
    #>   .. ..$ n_wls_below_w_up  : int
    #>   .. ..$ n_wls_above_w_up  : int
    #>   .. ..$ name_wl_below_w_do: chr
    #>   .. ..$ name_wl_above_w_do: chr
    #>   .. ..$ name_wl_below_w_up: chr
    #>   .. ..$ name_wl_above_w_up: chr
    #>   .. ..$ w_wl_below_w_do   : num
    #>   .. ..$ w_wl_above_w_do   : num
    #>   .. ..$ w_wl_below_w_up   : num
    #>   .. ..$ w_wl_above_w_up   : num
    #>   .. ..$ weight_up         : num
    #>   .. ..$ weight_do         : num
    #>   ..@ gauging_stations_missing: chr NA
    #>   ..@ comment                 : chr "Initialised by WaterLevelDataFrame()."
    #>   ..@ names                   : chr  "station" "station_int" "w"
    #>   ..@ row.names               : int  1 2 3 4 5 6 7 8 9 10 ...
    #>   ..@ .S3Class                : chr "data.frame"
    summary(wldf)
    #> $slots
    #>
    #> river                                                     Elbe
    #> time                                                2016-12-21
    #> gauging_stations                                          None
    #> gauging_stations_missing                                  None
    #> comment                  Initialised by WaterLevelDataFrame().
    #>
    #> $data
    #>     station       station_int           w
    #>  Min.   :257.0   Min.   :257000   Min.   : NA
    #>  1st Qu.:258.2   1st Qu.:258250   1st Qu.: NA
    #>  Median :259.5   Median :259500   Median : NA
    #>  Mean   :259.5   Mean   :259500   Mean   :NaN
    #>  3rd Qu.:260.8   3rd Qu.:260750   3rd Qu.: NA
    #>  Max.   :262.0   Max.   :262000   Max.   : NA
    #>                                   NA's   :51

The actual water level information is stored in the S4-slot `.Data`, which is in fact a `data.frame` with the columns `station`, `station_int` and `w`. The columns `station` and `station_int` contain a stationing information, which corresponds to the official stationing of the German Waterways and Shipping Administration (Wasserstraßen- und Schifffahrtsverwaltung (WSV)). The stationing information is duplicated to enable database joins with GIS data through the `integer`-type column `station_int`. The column `w` contains the actual water level in the height reference system DHHN92 (1992 German height reference system), but is usually empty after initialization and gets filled throught the application of one of the `waterLevel…()`-functions.

For the application of `waterLevel…()`-functions information stored in the S4-slots `river` and where appropriate `time` is essential. They enable a distinct localization of the stationing along the rivers Elbe and Rhine and a determination of the time of water level computation. The other slots of an object of class `WaterLevelDataFrame` are filled during the water level computation and contain partial results needed to visualize the results (`gauging_station`) or serve information purposes ( `gauging_stations_missing`, `comment`).

# Computation of water levels

## waterLevel

The most advanced function to interpolate FLYS3 water levels (Bundesanstalt für Gewässerkunde, 2013, 2016) with local gauging data is implemented in the `waterLevel()`-function. This function uses package-internal gauging data from the dataset `df.gauging_data`, which contains daily-averaged gauging data since 1960-01-01. Therefore `waterLevel()` can be applied for the time period between 1960-01-01 and yesterday.

After the initialization of a `WaterLevelDataFrame` the application is very simple:

    wldf <- waterLevel(wldf)
    summary(wldf)
    #> $slots
    #>
    #> river                                                Elbe
    #> time                                           2016-12-21
    #> gauging_stations         VOCKERODE, ROSSLAU, DESSAU, AKEN
    #> gauging_stations_missing                             None
    #> comment                         Computed by waterLevel().
    #>
    #> $data
    #>     station       station_int           w
    #>  Min.   :257.0   Min.   :257000   Min.   :54.42
    #>  1st Qu.:258.2   1st Qu.:258250   1st Qu.:54.67
    #>  Median :259.5   Median :259500   Median :54.82
    #>  Mean   :259.5   Mean   :259500   Mean   :54.87
    #>  3rd Qu.:260.8   3rd Qu.:260750   3rd Qu.:55.11
    #>  Max.   :262.0   Max.   :262000   Max.   :55.35

And if you want to visualize the results using `plotShiny()` the additional argument `shiny = TRUE` has to be used. Thereby the columns `section`, `weight_x` and `weight_y` get created in the `.Data`-Slot, which are required for visualization.

    wldf <- waterLevel(wldf, shiny = TRUE)
    summary(wldf)
    #> $slots
    #>
    #> river                                                Elbe
    #> time                                           2016-12-21
    #> gauging_stations         VOCKERODE, ROSSLAU, DESSAU, AKEN
    #> gauging_stations_missing                             None
    #> comment                         Computed by waterLevel().
    #>
    #> $data
    #>     station       station_int           w            section
    #>  Min.   :257.0   Min.   :257000   Min.   :54.42   Min.   :1.00
    #>  1st Qu.:258.2   1st Qu.:258250   1st Qu.:54.67   1st Qu.:2.00
    #>  Median :259.5   Median :259500   Median :54.82   Median :2.00
    #>  Mean   :259.5   Mean   :259500   Mean   :54.87   Mean   :2.02
    #>  3rd Qu.:260.8   3rd Qu.:260750   3rd Qu.:55.11   3rd Qu.:2.00
    #>  Max.   :262.0   Max.   :262000   Max.   :55.35   Max.   :3.00
    #>     weight_x           weight_y
    #>  Min.   :0.008197   Min.   :0.1000
    #>  1st Qu.:0.161765   1st Qu.:0.1911
    #>  Median :0.529412   Median :0.2640
    #>  Mean   :0.520203   Mean   :0.3192
    #>  3rd Qu.:0.897059   3rd Qu.:0.4294
    #>  Max.   :1.000000   Max.   :0.6302

    xlim_min <- 257
    xlim_max <- 263
    {
        plotShiny(wldf, TRUE, TRUE, TRUE, xlim = c(xlim_min, xlim_max),
                  xlab = "river station (km)",
                  ylab = "elevation (m a.s.l. (DHHN92))")
        legend("topright",
               col = c("darkblue", "darkblue", "darkblue", "red", "black"),
               pch = c(21, NA, NA, NA, NA),
               pt.bg = c("darkblue", NA, NA, NA, NA),
               pt.cex = c(1, NA, NA, NA, NA),
               lty = c(0, 0, 1, 1, 1),
               lwd = c(0, 0, 1, 0.6, 0.6),
               legend = c("gauge height", "gauge weight", "waterLevel",
                          "upper FLYS w.l.", "lower FLYS w.l."),
               text.col = c(1, "darkblue", 1, 1, 1),
               cex = 0.7, bty = "n")
    }

Fig. 1: Interpolated water level, computation-relevant stationary FLYS3 water levels (0.5MQ, a and 0.75MQ) and gauge height as of 2016-12-21 at River Elbe between Rosslau and Dessau.

## waterLevelPegelonline

The way how the `waterLevelPegelonline()`-function computes a water level is equivalent to the `waterLevel()`-function. Just the data source of the gauging data is different, since it does not use package-internal data, but online data provided by https://pegelonline.wsv.de/gast/start (Wasserstraßen- und Schifffahrtsverwaltung des Bundes (WSV), 2022). Because data provided by PEGELONLINE are only available for the past 30 days, the application of this function is limited to recent time periods, but with a high temporal resolution.

    # one hour ago
    time <- as.POSIXct(Sys.time() - 3600)

    # initialize a WaterLevelDataFrame
    wldf <- WaterLevelDataFrame(river   = "Elbe",
                                time    = time,
                                station = seq(257, 262, 0.1))

    # compute w
    wldf <- waterLevelPegelonline(wldf, shiny = TRUE)
    summary(wldf)
    #> $slots
    #>
    #> river                                                    Elbe
    #> time                               2025-12-02 17:14:12.950007
    #> gauging_stations             VOCKERODE, ROSSLAU, DESSAU, AKEN
    #> gauging_stations_missing                                 None
    #> comment                  Computed by waterLevelPegelonline().
    #>
    #> $data
    #>     station       station_int           w            section
    #>  Min.   :257.0   Min.   :257000   Min.   :53.83   Min.   :1.00
    #>  1st Qu.:258.2   1st Qu.:258250   1st Qu.:54.12   1st Qu.:2.00
    #>  Median :259.5   Median :259500   Median :54.29   Median :2.00
    #>  Mean   :259.5   Mean   :259500   Mean   :54.35   Mean   :2.02
    #>  3rd Qu.:260.8   3rd Qu.:260750   3rd Qu.:54.65   3rd Qu.:2.00
    #>  Max.   :262.0   Max.   :262000   Max.   :54.87   Max.   :3.00
    #>     weight_x           weight_y
    #>  Min.   :0.008197   Min.   :0.2276
    #>  1st Qu.:0.161765   1st Qu.:0.5395
    #>  Median :0.529412   Median :0.5881
    #>  Mean   :0.520203   Mean   :0.5528
    #>  3rd Qu.:0.897059   3rd Qu.:0.6667
    #>  Max.   :1.000000   Max.   :0.7453

    # and plot the results
    {
        plotShiny(wldf, TRUE, TRUE, TRUE, xlim = c(xlim_min, xlim_max),
                  xlab = "river station (km)",
                  ylab = "elevation (m a.s.l. (DHHN92))")
        legend("topright",
               col = c("darkblue", "darkblue", "darkblue", "red", "black"),
               pch = c(21, NA, NA, NA, NA),
               pt.bg = c("darkblue", NA, NA, NA, NA),
               pt.cex = c(1, NA, NA, NA, NA),
               lty = c(0, 0, 1, 1, 1),
               lwd = c(0, 0, 1, 0.6, 0.6),
               legend = c("gauge height", "gauge weight", "waterLevel",
                          "upper FLYS w.l.", "lower FLYS w.l."),
               text.col = c(1, "darkblue", 1, 1, 1),
               cex = 0.7, bty = "n")
    }

Fig. 2: Interpolated water level, computation-relevant stationary FLYS3 water levels (0.5MQ, a and 0.75MQ) and gauge height as of 2025-12-02 17:14 at River Elbe between Rosslau and Dessau.

## waterLevelFlood1 & waterLevelFlood2

To compare the newly developed functions `waterLevel()` and `waterLevelPegelonline()` to existing computation methods, the functions `waterLevelFlood1()` and `waterLevelFlood2()` have been implemented. These functions compute water levels according to the Flood1- and Flood2-methods of the modelling environment INFORM (Rosenzweig, Giebel, & Schleuter, 2011). They either shift the reference water level MQ vertically, so that it intersects with the gauge height at a selected reference gauging station, or linearly interpolate water levels with neighboring gauging stations.

    wldf <- WaterLevelDataFrame(river   = "Elbe",
                                time    = as.POSIXct("2016-12-21"),
                                station = seq(257, 262, 0.1))

    wldf1 <- waterLevelFlood1(wldf, "ROSSLAU", shiny = TRUE)
    summary(wldf1)
    #> $slots
    #>
    #> river                                                                                  Elbe
    #> time                                                                             2016-12-21
    #> gauging_stations                                                                    ROSSLAU
    #> gauging_stations_missing                                                               None
    #> comment                  Computed by waterLevelFlood1(): gauging_station = ROSSLAU, w = 137
    #>
    #> $data
    #>     station       station_int           w            section     weight_x
    #>  Min.   :257.0   Min.   :257000   Min.   :54.15   Min.   :1   Min.   :1
    #>  1st Qu.:258.2   1st Qu.:258250   1st Qu.:54.44   1st Qu.:1   1st Qu.:1
    #>  Median :259.5   Median :259500   Median :54.68   Median :1   Median :1
    #>  Mean   :259.5   Mean   :259500   Mean   :54.73   Mean   :1   Mean   :1
    #>  3rd Qu.:260.8   3rd Qu.:260750   3rd Qu.:55.05   3rd Qu.:1   3rd Qu.:1
    #>  Max.   :262.0   Max.   :262000   Max.   :55.36   Max.   :1   Max.   :1
    #>     weight_y
    #>  Min.   :-0.92
    #>  1st Qu.:-0.92
    #>  Median :-0.92
    #>  Mean   :-0.92
    #>  3rd Qu.:-0.92
    #>  Max.   :-0.92

    wldf2 <- waterLevelFlood1(wldf, "DESSAU", shiny = TRUE)
    summary(wldf2)
    #> $slots
    #>
    #> river                                                                                 Elbe
    #> time                                                                            2016-12-21
    #> gauging_stations                                                                    DESSAU
    #> gauging_stations_missing                                                              None
    #> comment                  Computed by waterLevelFlood1(): gauging_station = DESSAU, w = 165
    #>
    #> $data
    #>     station       station_int           w            section     weight_x
    #>  Min.   :257.0   Min.   :257000   Min.   :54.43   Min.   :1   Min.   :1
    #>  1st Qu.:258.2   1st Qu.:258250   1st Qu.:54.72   1st Qu.:1   1st Qu.:1
    #>  Median :259.5   Median :259500   Median :54.96   Median :1   Median :1
    #>  Mean   :259.5   Mean   :259500   Mean   :55.01   Mean   :1   Mean   :1
    #>  3rd Qu.:260.8   3rd Qu.:260750   3rd Qu.:55.33   3rd Qu.:1   3rd Qu.:1
    #>  Max.   :262.0   Max.   :262000   Max.   :55.64   Max.   :1   Max.   :1
    #>     weight_y
    #>  Min.   :-0.64
    #>  1st Qu.:-0.64
    #>  Median :-0.64
    #>  Mean   :-0.64
    #>  3rd Qu.:-0.64
    #>  Max.   :-0.64

    wldf3 <- waterLevelFlood2(wldf)
    summary(wldf3)
    #> $slots
    #>
    #> river                                                Elbe
    #> time                                           2016-12-21
    #> gauging_stations         VOCKERODE, ROSSLAU, DESSAU, AKEN
    #> gauging_stations_missing                             None
    #> comment                    Computed by waterLevelFlood2()
    #>
    #> $data
    #>     station       station_int           w
    #>  Min.   :257.0   Min.   :257000   Min.   :54.43
    #>  1st Qu.:258.2   1st Qu.:258250   1st Qu.:54.68
    #>  Median :259.5   Median :259500   Median :54.90
    #>  Mean   :259.5   Mean   :259500   Mean   :54.90
    #>  3rd Qu.:260.8   3rd Qu.:260750   3rd Qu.:55.12
    #>  Max.   :262.0   Max.   :262000   Max.   :55.35

Fig. 3: Water levels computed according to the Flood1-method with the reference gauges Rosslau (wldf1) and Dessau (wldf2) and the Flood2-method as of 2016-12-21 at River Elbe between Rosslau and Dessau.

## waterLevelFlys3InterpolateY

To compare the newly developed functions `waterLevel()` and `waterLevelPegelonline` to existing computation methods of FLYS3, the function `waterLevelFlys3InterpolateY()` has been implemented. This function computes a water level according to the method implemented in the W-INFO-module of FLYS3. This function determines the relative position of the gauge height at a reference gauge to the two surrounding FLYS3 water levels and uses this weight for a longitudinal interpolation between both water levels.

    wldf <- waterLevelFlys3InterpolateY(wldf, "ROSSLAU", shiny = TRUE)
    summary(wldf)
    #> $slots
    #>
    #> river                                                                                             Elbe
    #> time                                                                                        2016-12-21
    #> gauging_stations                                                                               ROSSLAU
    #> gauging_stations_missing                                                                          None
    #> comment                  Computed by waterLevelFlys3InterpolateY(): gauging_station = ROSSLAU, w = 137
    #>
    #> $data
    #>     station       station_int           w            section     weight_x
    #>  Min.   :257.0   Min.   :257000   Min.   :54.11   Min.   :1   Min.   :1
    #>  1st Qu.:258.2   1st Qu.:258250   1st Qu.:54.42   1st Qu.:1   1st Qu.:1
    #>  Median :259.5   Median :259500   Median :54.67   Median :1   Median :1
    #>  Mean   :259.5   Mean   :259500   Mean   :54.72   Mean   :1   Mean   :1
    #>  3rd Qu.:260.8   3rd Qu.:260750   3rd Qu.:55.08   3rd Qu.:1   3rd Qu.:1
    #>  Max.   :262.0   Max.   :262000   Max.   :55.35   Max.   :1   Max.   :1
    #>     weight_y
    #>  Min.   :0.1786
    #>  1st Qu.:0.1786
    #>  Median :0.1786
    #>  Mean   :0.1786
    #>  3rd Qu.:0.1786
    #>  Max.   :0.1786

Fig. 4: Water levels according to FLYS3 with the reference gauge Rosslau as of 2016-12-21 at River Elbe between Rosslau and Dessau.

## waterLevelFlys3…

All other `waterLevelFlys3...()`-functions (`waterLevelFlys3()`, `waterLevelFlys3Seq()` and `waterLevelFlys3InterpolateX()`) serve exclusively for the preparation and querying of stationary FLYS3 water levels. They can be used to extract water levels from the dataset `df.flys` and interpolate the water levels linearly along the x-axis, but without modifying the dataset contents. These functionalities are needed for all `waterLevel…()`-functions described in this vignette and are mentioned here for completeness.

# Products

## waterLevel

The `waterLevel()` function is the central function of the package hyd1d. To apply it only three input parameters, that are needed to initialize a `WaterLevelDataFrame`, are required. That predestines this function to embed it into an interactive Shiny Application:

https://shiny.bafg.de/waterlevel/

Fig. 5: Screenshot of the waterLevel-ShinyApp with the interpolated water level, caomputationrevelvant stationary FLYS3-waterlevels (0.5MQ, a and 0.75MQ) and gauge heights at 2016-12-21 at the River Elbe between Rosslau and Dessau, Germany.

## waterLevelPegelonline

The same is true for the function `waterLevelPegelonline()`. Since this function queries gauging data through the internet and not from package-internal datasets, the resulting shiny application is well suited to generate up-to-date water level information.

https://shiny.bafg.de/waterlevelpegelonline/

Fig. 6: Screenshot of the waterLevelPegelonline-ShinyApp with the interpolated water level, computationrevelvant stationary FLYS3-waterlevels (a, 0.75MQ and 0.5MQ) and gauge heights at 2018-04-13 11:00 a.m. at the River Elbe between Rosslau and Dessau, Germany.

## hydflood (flood extents and durations)

The R package hydflood enables the modelling of flood extents and durations through an extrapolation of water levels computed along the river axis with functions provided within R package hyd1d to cross section areas and GIS operations comparing these water levels to digital elevation models. Daily flood extents can be aggregated over longer time periods to flood durations (e.g. days/year). More details to this method can be found on the corresponding package documentation of hydflood:

https://hydflood.bafg.de

# References

Bundesanstalt für Gewässerkunde. (2013). FLYS goes WEB: Eröffnung eines neuen hydrologischen Fachdienstes in der BfG. Bundesanstalt für Gewässerkunde, Koblenz, Germany. https://doi.org/10.5675/BfG_Veranst_2013.4
Bundesanstalt für Gewässerkunde. (2016). FLYS – Flusshydrologischer Webdienst. Bundesanstalt für Gewässerkunde, Koblenz, Germany. https://www.bafg.de/DE/5_Informiert/1_Portale_Dienste/FLYS/flys_node.html
Rosenzweig, S., Giebel, H., & Schleuter, M. (2011). Ökologische Modellierungen für die Wasser- und Schifffahrtsverwaltung – Das integrierte Flussauenmodell INFORM in seiner neuesten Fassung (Version 3). Bundesanstalt für Gewässerkunde, Koblenz, Germany. https://doi.org/10.5675/bfg-1667
Wasserstraßen- und Schifffahrtsverwaltung des Bundes (WSV). (2022). PEGELONLINE. https://pegelonline.wsv.de/gast/start