Source code for xlandsat._interpolation
# Copyright (c) 2022 The xlandsat developers.
# Distributed under the terms of the MIT License.
# SPDX-License-Identifier: MIT
"""
Interpolation methods for scenes.
"""
import numpy as np
import scipy as sp
[docs]
def interpolate_missing(scene, pixel_radius=20):
"""
Fill missing values (NaNs) in a scene by cubic interpolation
Each missing value is filled by interpolating the pixels within a
neighboring region (controlled by ``pixel_radius``) using a piecewise cubic
2D interpolator. Interpolation is done for each band in a scene separately.
Note that this is mostly good if there are a few missing values, not large
regions of the scene.
Parameters
----------
scene : :class:`xarray.Dataset`
A Landsat scene, as read with :func:`xlandsat.load_scene`.
pixel_radius : int
Number of pixels to the east, west, south, and north of a missing value
that will be used for interpolation. Smaller values make for faster
interpolation but may lead to bad results if many missing values are
grouped together.
Returns
-------
filled_scene : :class:`xarray.Dataset`
The scene with missing values filled in.
"""
filled_scene = scene.copy(deep=True)
rows, columns = np.meshgrid(
np.arange(scene.northing.size),
np.arange(scene.easting.size),
indexing="ij",
)
for band in scene:
values = filled_scene[band].values
nans = np.isnan(values)
for i, j in zip(rows[nans], columns[nans]):
imin, imax = _search_range(i, pixel_radius, scene.northing.size)
jmin, jmax = _search_range(j, pixel_radius, scene.easting.size)
valid = ~np.isnan(values[imin:imax, jmin:jmax])
interpolator = sp.interpolate.CloughTocher2DInterpolator(
(
rows[imin:imax, jmin:jmax][valid],
columns[imin:imax, jmin:jmax][valid],
),
values[imin:imax, jmin:jmax][valid],
)
values[i, j] = interpolator(i, j)
return filled_scene
def _search_range(index, pixel_radius, dim_size):
"""
Get a valid range around the index to search for interpolation data.
Mostly used to avoid overflowing the image boundaries.
"""
left, right = index - pixel_radius, index + pixel_radius
if left < 0:
left = 0
if right > dim_size:
right = dim_size
return left, right