B-spline interpolation function

If the B-spline structure is defined, it can be used to define the B-spline interpolation function. In this chapter, we will continue with the examples from the B-spline structure section for the one and two dimensional case.

One dimensional case

In the B-spline structure section, we began with the example file rhine_data_cubic-nak.jl from the examples folder where we already defined the B-spline structure for the cubic B-spline interpolation with not-a-knot end condition and smoothing.

# Include packages
using TrixiBottomTopography
using CairoMakie

# Define data path
root_dir = pkgdir(TrixiBottomTopography)
data = joinpath(root_dir, "examples", "data", "rhine_data_1d_20_x.txt")

# Define B-spline structure
spline_struct = CubicBSpline(data; end_condition = "not-a-knot", smoothing_factor = 999)
CubicBSpline{Vector{Float64}, Float64, Vector{Float64}, Matrix{Int64}}([357000.0, 357020.0, 357040.0, 357060.0, 357080.0, 357100.0, 357120.0, 357140.0, 357160.0, 357180.0  …  357800.0, 357820.0, 357840.0, 357860.0, 357880.0, 357900.0, 357920.0, 357940.0, 357960.0, 357980.0], 20.0, [46.91426684256018, 47.397904854124576, 46.99689666444544, 46.50889411070535, 46.73154903008692, 46.862924837363984, 46.74903591362994, 46.40702226591983, 46.31904390610859, 46.654018220049146  …  39.07284424693978, 39.1231081223708, 39.13738000648763, 39.136760208084766, 39.10325245367299, 39.08822484618267, 39.09410545889054, 39.1030246596446, 39.08484073202447, 39.009411959609665], [-1 3 -3 1; 3 -6 3 0; -3 0 3 0; 1 4 1 0])

To define the B-spline interpolation function for a variable x, we use the spline_interpolation function. For the one dimensional case, this function is implemented as spline_interpolation(linear_struct, x) and spline_interpolation(cubic_struct, x).

# Define B-spline interpolation function
spline_func(x) = spline_interpolation(spline_struct, x)

This defines the cubic B-spline interpolation function with not-a-knot end condition and smoothing with respect to variable x from the previously created spline_struct. If we want to visualize the interpolation function with 100 interpolation points, we define the following:

# Define interpolation points
n = 200
x_int_pts = Vector(LinRange(spline_struct.x[1], spline_struct.x[end], n))
200-element Vector{Float64}:
 357000.0
 357004.92462311557
 357009.84924623114
 357014.77386934677
 357019.6984924623
 357024.6231155779
 357029.5477386934
 357034.47236180905
 357039.3969849247
 357044.3216080402
      ⋮
 357940.6030150754
 357945.52763819095
 357950.4522613065
 357955.3768844221
 357960.3015075377
 357965.2261306533
 357970.15075376886
 357975.07537688443
 357980.0

and evaluate to obtain the corresponding y values by:

# Get interpolated values
y_int_pts = spline_func.(x_int_pts)
200-element Vector{Float64}:
 47.25046382058398
 47.2358035685696
 47.179415507626665
 47.0932077352213
 46.98908834882418
 46.87896544589783
 46.774747123914494
 46.68834148033668
 46.63165661263463
 46.61391092263422
  ⋮
 39.09483624644603
 39.09666708631454
 39.098177071127225
 39.098916216968526
 39.09843453992283
 39.09628205607458
 39.09200878150819
 39.08516473230809
 39.07529992455869

Plotting the interpolated points can be done via the command below and produces a one-dimensional plot

plot_topography(x_int_pts, y_int_pts; xlabel = "ETRS89 East", ylabel = "DHHN2016 Height")
Example block output

Alternatively, one can plot the interpolated bottom topography together with the interpolation knots as follows

# Get the original interpolation knots
x_knots = spline_struct.x
y_knots = spline_func.(x_knots)

plot_topography_with_interpolation_knots(x_int_pts, y_int_pts, x_knots, y_knots;
                                         xlabel = "ETRS89 East", ylabel = "DHHN2016 Height")
Example block output

Two dimensional case

In the B-spline structure section, we examined the example file rhine_data_bicubic-nak.jl from the examples folder where we already created the B-spline structure for the bicubic B-spline interpolation with not-a-knot end condition and smoothing.

# Include packages
using TrixiBottomTopography
using CairoMakie

# Define data path
root_dir = pkgdir(TrixiBottomTopography)
data = joinpath(root_dir, "examples", "data", "rhine_data_2d_20.txt")

# Define B-spline structure
spline_struct = BicubicBSpline(data; end_condition = "not-a-knot", smoothing_factor = 9999)
BicubicBSpline{Vector{Float64}, Vector{Float64}, Float64, Matrix{Float64}, Matrix{Int64}}([357000.0, 357020.0, 357040.0, 357060.0, 357080.0, 357100.0, 357120.0, 357140.0, 357160.0, 357180.0  …  357800.0, 357820.0, 357840.0, 357860.0, 357880.0, 357900.0, 357920.0, 357940.0, 357960.0, 357980.0], [5.646019e6, 5.646039e6, 5.646059e6, 5.646079e6, 5.646099e6, 5.646119e6, 5.646139e6, 5.646159e6, 5.646179e6, 5.646199e6  …  5.646819e6, 5.646839e6, 5.646859e6, 5.646879e6, 5.646899e6, 5.646919e6, 5.646939e6, 5.646959e6, 5.646979e6, 5.646999e6], 20.0, [46.600451320002854 47.282399695552705 … 47.09971997322787 47.10735082392629; 45.82016087061047 46.502109246160636 … 46.963741530201055 46.97137238089921; … ; 45.99186284178017 45.686484464847375 … 39.098457400873656 39.23988800954714; 46.243985904572256 45.938607527639306 … 39.06537978820518 39.206810396878865], [-1 3 -3 1; 3 -6 3 0; -3 0 3 0; 1 4 1 0])

To define the B-spline interpolation function for variables x and y, we use the spline_interpolation function for the two dimensional case. This functionality is implemented as spline_interpolation(bilinear_struct, x, y) and spline_interpolation(bicubic_struct, x, y).

# Define B-spline interpolation function
spline_func(x,y) = spline_interpolation(spline_struct, x, y)

This defines the bicubic B-spline interpolation function with not-a-knot end condition and smoothing for variables x and y due to the previously constructed spline_struct. If we want to visualize the bicubic interpolation function with 100 interpolation points in each spatial direction, we define:

# Define interpolation points
n = 100
x_int_pts = Vector(LinRange(spline_struct.x[1], spline_struct.x[end], n))
y_int_pts = Vector(LinRange(spline_struct.y[1], spline_struct.y[end], n))
100-element Vector{Float64}:
 5.646019e6
 5.646028898989899e6
 5.646038797979798e6
 5.646048696969697e6
 5.646058595959596e6
 5.646068494949495e6
 5.646078393939394e6
 5.646088292929293e6
 5.646098191919192e6
 5.646108090909091e6
 ⋮
 5.646919808080808e6
 5.646929707070707e6
 5.646939606060606e6
 5.646949505050505e6
 5.646959404040404e6
 5.646969303030303e6
 5.646979202020202e6
 5.646989101010101e6
 5.646999e6

To fill a matrix z_int_pts, which contains the corresponding z values for x_int_pts and y_int_pts, we use the helper function evaluate_bicubicspline_interpolant.

# Get interpolated matrix
z_int_pts = evaluate_bicubicspline_interpolant(spline_func, x_int_pts, y_int_pts)
100×100 Matrix{Float64}:
 46.4652  46.0495  45.59    45.0608  …  45.5853  45.6011  45.6461  45.719
 46.7486  46.3415  45.8956  45.3881     45.556   45.5411  45.557   45.6029
 46.9957  46.6121  46.1894  45.7059     45.5651  45.5146  45.4976  45.5147
 47.1991  46.8501  46.4565  46.0011     45.6092  45.5245  45.4746  45.4658
 47.3511  47.0408  46.6819  46.2619     45.6843  45.5739  45.4969  45.468
 47.4475  47.1729  46.8535  46.4774  …  45.79    45.6678  45.5745  45.5312
 47.4999  47.2541  46.9726  46.642      45.9414  45.8197  45.717   45.6593
 47.5231  47.2966  47.0444  46.7527     46.1512  46.0398  45.9296  45.8516
 47.5254  47.3085  47.0747  46.8123     46.3974  46.3073  46.1955  46.0947
 47.5134  47.2972  47.0707  46.8264     46.6459  46.5899  46.4879  46.369
  ⋮                                  ⋱                             
 46.9407  46.8666  46.7907  46.7222     39.06    39.0671  39.071   39.072
 46.8836  46.8109  46.7421  46.682      39.0394  39.049   39.0552  39.0585
 46.848   46.7785  46.7166  46.6635     39.019   39.031   39.0395  39.0445
 46.8304  46.7651  46.7088  46.6602     39.0024  39.0165  39.027   39.0328
 46.8323  46.7707  46.7164  46.6678  …  38.9936  39.0082  39.0196  39.0259
 46.854   46.7937  46.7365  46.6822     38.9969  39.0093  39.02    39.0263
 46.8873  46.8252  46.7617  46.6978     39.0192  39.0257  39.033   39.0377
 46.922   46.8547  46.7832  46.7084     39.0675  39.0638  39.0642  39.0638
 46.9479  46.8731  46.7926  46.7075     39.1495  39.1302  39.1184  39.1086

Plotting the interpolated values gives the representation directly below the plot_topography command

plot_topography(x_int_pts, y_int_pts, z_int_pts;
                xlabel="ETRS89\n East",
                ylabel="ETRS89\n North",
                zlabel="DHHN2016\n Height",
                azimuth_angle = 54 * pi / 180,
                elevation_angle = 27 * pi / 180)
Example block output

Alternatively, one can plot the interpolated two-dimensional bottom topography together with the interpolation knots as follows

# Get the original interpolation knots
x_knots = spline_struct.x
y_knots = spline_struct.y
z_knots = evaluate_bicubicspline_interpolant(spline_func, x_knots, y_knots)

plot_topography_with_interpolation_knots(x_int_pts, y_int_pts, z_int_pts,
                                         x_knots, y_knots, z_knots;
                                         xlabel="ETRS89\n East",
                                         ylabel="ETRS89\n North",
                                         zlabel="DHHN2016\n Height",
                                         azimuth_angle = 54 * pi / 180,
                                         elevation_angle = 27 * pi / 180)
Example block output