Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add parallax correction via new ParallaxCorrectionModifier #1904

Merged
merged 129 commits into from Jul 28, 2022
Merged
Show file tree
Hide file tree
Changes from 18 commits
Commits
Show all changes
129 commits
Select commit Hold shift + click to select a range
8f1257f
Parallax correction
May 5, 2021
58a960a
Added projection
May 6, 2021
342030f
Added projection
May 6, 2021
a49855c
better error message
uhamann Aug 23, 2021
2a796de
parallax example script
uhamann Aug 24, 2021
77f5e50
Merge branch 'master' into parallax
uhamann Aug 24, 2021
daf4400
Started unit test for parallax correction
gerritholl Nov 29, 2021
eebe910
Merge remote-tracking branch 'remotes/tmp/parallax' into parallax-cor…
gerritholl Nov 29, 2021
db6ec84
Merge parallax.py into satpy
gerritholl Nov 29, 2021
ae72e98
Merge remote-tracking branch 'remotes/tmp/parallax' into parallax-cor…
gerritholl Nov 30, 2021
a9d5668
Adapt parallax dependency from meteoswiss
gerritholl Nov 30, 2021
c3918bf
Complement AUTHORS.md
gerritholl Nov 30, 2021
1b38fb8
Add Satpy GPL headers to new source files
gerritholl Nov 30, 2021
c321b18
Extend unit tests for parallax correction.
gerritholl Nov 30, 2021
5dae1e6
More unit tests for parallax correction
gerritholl Nov 30, 2021
70292ae
Remove some unused code
gerritholl Dec 1, 2021
2cf131a
Extend parallax unit tests
gerritholl Dec 2, 2021
a9aebd0
Cleaner parallax correction implementation
Dec 2, 2021
913adaf
Merge Jussis simplification back in parallax.py
gerritholl Dec 3, 2021
5ffc618
Improve parameterised unit tests
gerritholl Dec 3, 2021
1077d7a
Add more unit tests for parallax correction
gerritholl Dec 3, 2021
eed47ec
Extend unit tests
gerritholl Dec 3, 2021
b0a2c68
Merge branch 'main' into parallax-correction
gerritholl Dec 3, 2021
0850114
Switch to nearest neighbour
gerritholl Dec 6, 2021
7960f1b
Add more tests for parallax correction
gerritholl Dec 6, 2021
e017866
Restore slant cloudy test
gerritholl Dec 7, 2021
714c110
Use Simons reference value
gerritholl Dec 7, 2021
b30f8bc
Merge branch 'main' into parallax-correction
gerritholl Dec 14, 2021
0c45d39
Fix swapped lon/lat in unit test
gerritholl Dec 14, 2021
06a5716
Add parameterised fixture for generating a cloud.
gerritholl Dec 14, 2021
b54a431
Merge branch 'main' into parallax-correction
gerritholl Jan 3, 2022
e713e89
Add more detailed tests
gerritholl Jan 3, 2022
9c1404b
Add tolerance to distance comparison
gerritholl Jan 3, 2022
6518d19
Add tests for no or partly overlapping CTH
gerritholl Jan 5, 2022
c2de3da
Allow costumisation of resampling method in parallax correction
gerritholl Jan 6, 2022
4ed3aa9
Add tests with dask and same areas
gerritholl Jan 7, 2022
7854191
Point out that CompositeBase is also for modifiers
gerritholl Jan 7, 2022
90d57e1
Started work on ParallaxCorrectionModifier class
gerritholl Jan 7, 2022
f149eba
Add modifier interface for parallax correction
gerritholl Jan 7, 2022
ee97200
Mark partly/no overlap as xfail
gerritholl Jan 7, 2022
f7d692f
Merge branch 'main' into parallax-correction
gerritholl Jan 7, 2022
07e7731
Improve parallax-correction docs
gerritholl Jan 7, 2022
d987fc1
Adapt to pre-3.9 syntax
gerritholl Jan 7, 2022
25da335
Merge branch 'improve-modifier-docs' into parallax-correction
gerritholl Jan 7, 2022
33df7c1
Add documentation on parallax correction modifier
gerritholl Jan 7, 2022
cc01a86
Improve parallax correction documentation
gerritholl Jan 7, 2022
d6a9cc0
Merge branch 'improve-modifier-docs' into parallax-correction
gerritholl Jan 7, 2022
7df874b
Remove unused function and TODO
gerritholl Jan 7, 2022
6e94b77
Repair forward parallax after elevation change
gerritholl Jan 7, 2022
a9022e0
More specific parameter failure marking
gerritholl Jan 10, 2022
bf56335
Mark one more case as xfail
gerritholl Jan 10, 2022
b5340da
Write log message when doing parallax correction
gerritholl Jan 11, 2022
688e2c7
Add backup calculation of orbital parameters
gerritholl Jan 12, 2022
17479d9
Adapt to Python 3.7 syntax
gerritholl Jan 12, 2022
0a8e515
Add missing dependencies for continuous integration
gerritholl Jan 12, 2022
75f2e2d
Try unstable test with latest astropy
gerritholl Jan 12, 2022
0ea5bbb
Improve example and documentation for parallax composite
gerritholl Jan 12, 2022
fc3c94e
More informative debug message
gerritholl Jan 17, 2022
e43c499
Add failing test for NWCSAF confetti bug
gerritholl Jan 20, 2022
6236340
Add debug mode to parallax correction
gerritholl Jan 26, 2022
c75681f
Consistent use metres above Earth's surface
gerritholl Jan 27, 2022
eeb52a4
Remember that satellite altitude reported in km
gerritholl Jan 27, 2022
5c4089c
Remove destructive preprocess method
gerritholl Jan 27, 2022
e20ad8f
Verify cloud moves toward observer
gerritholl Jan 28, 2022
c96dd78
Avoid creating a scene when resampling
gerritholl Jan 28, 2022
c4ae9d1
Improve test confirm cloud moves south
gerritholl Jan 28, 2022
65e2533
Fix cloud direction movement test
gerritholl Jan 28, 2022
8047bc5
Perform test with different radii of influence
gerritholl Jan 28, 2022
ff40e6a
Clarify comments
gerritholl Feb 2, 2022
6e4e3d1
Set cloudfree to 0 CTH and try other things
gerritholl Feb 3, 2022
a73b31a
Try with bucket resampler
gerritholl Feb 3, 2022
22ae499
Change parallax resampling approach
gerritholl Feb 4, 2022
bd8b5a9
Add test confirming new cloud area fully cloudy
gerritholl Feb 4, 2022
a67a206
Fix get_abs_max implementation
gerritholl Feb 4, 2022
cffb1d2
Resample CTH to target area first
gerritholl Feb 4, 2022
e539415
Ensure test case has valid polar area
gerritholl Feb 4, 2022
568b551
Also use 3575 at antimeridian
gerritholl Feb 4, 2022
2f18c60
Fix tests for 15 km cloud
gerritholl Feb 7, 2022
7b67560
Add test with dask arrays
gerritholl Feb 7, 2022
6766f90
make parallax correction more dask-friendly
gerritholl Feb 7, 2022
17d7468
Re-arrange and extend parallax unit tests
gerritholl Feb 7, 2022
fd3a2ac
Rename variable
gerritholl Feb 8, 2022
4e4ce01
In debug mode, store counts as dataset not ndarray
gerritholl Feb 8, 2022
bea71a3
Fix radius of influence in final resampling step
gerritholl Feb 8, 2022
e9e8b20
Merge branch 'main' into parallax-correction
gerritholl Mar 18, 2022
a3765c6
Change version added for parallax correction
gerritholl Mar 18, 2022
8a23932
Refactor parallax correction tests
gerritholl Mar 22, 2022
c452e7d
Merge branch 'main' into parallax-correction
gerritholl Mar 23, 2022
1297774
Merge branch 'main' into parallax-correction
gerritholl Apr 6, 2022
e4fd0c9
Merge branch 'main' into parallax-correction
gerritholl Apr 7, 2022
fe4f938
Merge branch 'main' into parallax-correction
gerritholl Apr 11, 2022
173ed44
Make sure to call apply_modifier_info
gerritholl Apr 11, 2022
ddfebcb
Add test for double-load bug
gerritholl Apr 12, 2022
d4edc4c
Add test confirming no compute occurs.
gerritholl Apr 12, 2022
933dac6
Fix example and test YAML config
gerritholl Apr 12, 2022
f60b0ad
Downgrade syntax to support Python 3.8
gerritholl Apr 12, 2022
cb15600
refactor unit test test_modifier_interface_cloud_moves_to_observer
gerritholl Apr 12, 2022
11fee22
refactor test_correct_area_clearsky
gerritholl Apr 12, 2022
d585dbd
Merge branch 'main' into parallax-correction
gerritholl Apr 14, 2022
58f55b6
Change versionadded to 0.37
gerritholl Apr 14, 2022
c3e393d
Make sure we're getting dask arrays from get_lonlats
gerritholl May 4, 2022
5c3a4f0
Refactoring and docstring improvements
gerritholl May 5, 2022
5323363
Add more prominent "experimental" warning
gerritholl May 5, 2022
259a0a2
Fix filename for pc docstring example
gerritholl May 6, 2022
ed38cda
Point out pc only supported for GEO
gerritholl May 6, 2022
26564f5
Move get_satpos_from_name to satpy.utils
gerritholl May 6, 2022
48690ca
Needs pyresample 1.24
gerritholl May 6, 2022
bb2040a
Change variable name for bucket resampler instance
gerritholl Jun 17, 2022
afd5951
Merge branch 'main' into parallax-correction
gerritholl Jul 11, 2022
0b3ea1c
Mock TLE downloading in parallax correction unit tests
gerritholl Jul 11, 2022
1230822
add another missing socket mock
gerritholl Jul 11, 2022
02bbda3
Merge branch 'main' into parallax-correction
gerritholl Jul 11, 2022
e78a9f8
Minor change to satisfy codebeat
gerritholl Jul 11, 2022
2aac451
Add extras dependency for skyfield and astropy
gerritholl Jul 25, 2022
1d953a8
Describe version, not only year
gerritholl Jul 25, 2022
41e6ec5
Move units note from comment to docstring
gerritholl Jul 25, 2022
7d8ff7b
Write "degrees" rather than "°"
gerritholl Jul 25, 2022
d874994
typo: or → on
gerritholl Jul 25, 2022
5e37359
Change opening line of class docstring.
gerritholl Jul 25, 2022
9509274
Add some more flexibility in parallax correction
gerritholl Jul 25, 2022
4e97767
Improve configurability in parallax correction
gerritholl Jul 26, 2022
567d27a
Add images with and without parallax correction
gerritholl Jul 26, 2022
2c3a5d7
Fix duplicate doc and rename parameter
gerritholl Jul 27, 2022
5ca49c1
Parallax docstring clarification
gerritholl Jul 27, 2022
66f02f1
Adapt unit test in test_utils
gerritholl Jul 27, 2022
a8fce07
Simplify docstring for forward_parallax
gerritholl Jul 27, 2022
5130cdb
Utility function for parallax displacement + docs
gerritholl Jul 28, 2022
dc352e3
Small docstring fixes
gerritholl Jul 28, 2022
9b5e980
Add image showing parallax magnitude for GOES-East
gerritholl Jul 28, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
9 changes: 5 additions & 4 deletions AUTHORS.md
Expand Up @@ -25,13 +25,13 @@ The following people have made contributions to this project:
- [Adam Dybbroe (adybbroe)](https://github.com/adybbroe)
- [Ulrik Egede (egede)](https://github.com/egede)
- [Joleen Feltz (joleenf)](https://github.com/joleenf)
- [Stephan Finkensieper (sfinkens)](https://github.com/sfinkens)
- [Stephan Finkensieper (sfinkens)](https://github.com/sfinkens) - Deutscher Wetterdienst
mraspaud marked this conversation as resolved.
Show resolved Hide resolved
- [Andrea Grillini (AppLEaDaY)](https://github.com/AppLEaDaY)
- [Blanka Gvozdikova (gvozdikb)](https://github.com/gvozdikb)
- [Nina Håkansson (ninahakansson)](https://github.com/ninahakansson)
- [Ulrich Hamann](https://github.com/)
- [Mitch Herbertson (mherbertson)](https://github.com/mherbertson)
- [Gerrit Holl (gerritholl)](https://github.com/gerritholl)
- [Gerrit Holl (gerritholl)](https://github.com/gerritholl) - Deutscher Wetterdienst
- [David Hoese (djhoese)](https://github.com/djhoese)
- [Marc Honnorat (honnorat)](https://github.com/honnorat)
- [Mikhail Itkin (mitkin)](https://github.com/mitkin)
Expand All @@ -42,7 +42,8 @@ The following people have made contributions to this project:
- [Janne Kotro (jkotro)](https://github.com/jkotro)
- [Ralph Kuehn (ralphk11)](https://github.com/ralphk11)
- [Panu Lahtinen (pnuu)](https://github.com/pnuu)
- [Thomas Leppelt (m4sth0)](https://github.com/m4sth0)
- [Jussi Leinonen (jleinonen)](https://github.com/jleinonen) - meteoswiss
- [Thomas Leppelt (m4sth0)](https://github.com/m4sth0) - Deutscher Wetterdienst
- [Lu Liu (yukaribbba)](https://github.com/yukaribbba)
- [Andrea Meraner (ameraner)](https://github.com/ameraner)
- [Aronne Merrelli (aronnem)](https://github.com/aronnem)
Expand Down Expand Up @@ -76,4 +77,4 @@ The following people have made contributions to this project:
- [oananicola (oananicola)](https://github.com/oananicola)
- [praerien (praerien)](https://github.com/praerien)
- [Xin Zhang (zxdawn)](https://github.com/zxdawn)
- [Yufei Zhu (yufeizhu600)](https://github.com/yufeizhu600)
- [Yufei Zhu (yufeizhu600)](https://github.com/yufeizhu600)
5 changes: 5 additions & 0 deletions satpy/modifiers/geometry.py
Expand Up @@ -175,3 +175,8 @@ def __init__(self, correction_limit=88., **kwargs):
def _apply_correction(self, proj, coszen):
logger.debug("Apply the effective solar atmospheric path length correction method by Li and Shibata")
return atmospheric_path_length_correction(proj, coszen, limit=self.correction_limit, max_sza=self.max_sza)


def parallax_correction(sc):
"""Apply parallax correction to entire scene."""
return sc
gerritholl marked this conversation as resolved.
Show resolved Hide resolved
246 changes: 246 additions & 0 deletions satpy/modifiers/parallax.py
@@ -0,0 +1,246 @@
# Copyright (c) 2021 Satpy developers
#
# This file is part of satpy.
#
# satpy is free software: you can redistribute it and/or modify it under the
# terms of the GNU General Public License as published by the Free Software
# Foundation, either version 3 of the License, or (at your option) any later
# version.
#
# satpy is distributed in the hope that it will be useful, but WITHOUT ANY
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
# A PARTICULAR PURPOSE. See the GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License along with
# satpy. If not, see <http://www.gnu.org/licenses/>.
"""Parallax correction.

Routines related to parallax correction using datasets involving height, such
as cloud top height.

The geolocation of (geostationary) satellite imagery is calculated
on the assumption of a clear view from the satellite to the surface.
When a cloud blocks the view of the surface, the geolocation is not
accurate for the cloud top. This module contains routines to project
the geolocation onto the cloud top.

"""

from datetime import datetime

import numpy as np
import xarray as xr
from pyorbital.orbital import A as EARTH_RADIUS
from pyorbital.orbital import get_observer_look
from pyresample.geometry import SwathDefinition
from scipy.signal import convolve

from satpy.utils import get_satpos, lonlat2xyz, xyz2lonlat

from . import projection


def forward_parallax(sat_lon, sat_lat, sat_alt, lon, lat, height):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this based on a book/paper? If so, please specify a reference.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can @jleinonen answer this one?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ping @jleinonen can you say something about the source? Or just purely geometry derived from first principles?

"""Calculate forward parallax effect.
gerritholl marked this conversation as resolved.
Show resolved Hide resolved

Calculate the forward parallax effect. When a satellite instrument
observes the Earth, the geolocation assumes it sees the Earth surface at
the geoid (elevation zero). In reality, the ray may stop short of the
geoid as it observes, for example, a cloud or elevated ground. This
function calculates the forward parallax effect. If the view of a pixel at
location (lat, lon) is blocked by a cloud at height h, we calculate the
location of this blocking.

Calculate parallax correction based on satellite position and
(cloud top) height coordinates in geodetic (unprojected) coordinates.
This function calculates the latitude and longitude belonging to the
cloud top, based on the location of the satellite and the location
of the cloud.

For scenes that are only partly cloudy, the user might set the cloud top
height for clear-sky pixels to NaN. This function will return a corrected
lat/lon as NaN as well. The user can use the original lat/lon for those
pixels or use the higher level :class:`ParallaxCorrection` class.

This function assumes a spherical Earth.

Args:
sat_lon (number): Satellite longitude in geodetic coordinates [°]
sat_lat (number): Satellite latitude in geodetic coordinates [°]
sat_alt (number): Satellite altitude above the Earth surface [km]
lon (array or number): Longitudes of pixel or pixels to be corrected,
in geodetic coordinates [°]
lat (array or number): Latitudes of pixel/pixels to be corrected, in
geodetic coordinates [°]
height (array or number): Heights of pixels on which the correction
will be based. Typically this is the cloud based height. [km]

Returns:
(corrected_lon, corrected_lat): New geolocation for the longitude and
latitude that were to be corrected, in geodetic coordinates. [°]
"""
X_sat = np.hstack(lonlat2xyz(sat_lon, sat_lat)) * sat_alt
X = np.stack(lonlat2xyz(lon, lat), axis=-1) * EARTH_RADIUS
gerritholl marked this conversation as resolved.
Show resolved Hide resolved
# the datetime doesn't actually affect the result but is required
# so we use a placeholder
gerritholl marked this conversation as resolved.
Show resolved Hide resolved
(_, elevation) = get_observer_look(
sat_lon, sat_lat, sat_alt,
datetime(2000, 1, 1), lon, lat, EARTH_RADIUS)
# TODO: handle cases where this could divide by 0
parallax_distance = height / np.sin(np.deg2rad(elevation))
gerritholl marked this conversation as resolved.
Show resolved Hide resolved

X_d = X - X_sat
sat_distance = np.sqrt((X_d*X_d).sum(axis=-1))
dist_shape = X_d.shape[:-1] + (1,) # force correct array broadcasting
X_top = X - X_d*(parallax_distance/sat_distance).reshape(dist_shape)

(corrected_lon, corrected_lat) = xyz2lonlat(
X_top[..., 0], X_top[..., 1], X_top[..., 2])
return (corrected_lon, corrected_lat)


class ParallaxCorrection:
"""Class for parallax corrections.
gerritholl marked this conversation as resolved.
Show resolved Hide resolved

This class contains higher-level functionality to wrap the parallal
gerritholl marked this conversation as resolved.
Show resolved Hide resolved
correction calculations in :func:`forward_parallax`. The class is
initialised using a base area, which is the area for which a corrected
geolocation will be calculated. The resulting object is a callable.
Calling the object with an array of (cloud top) heights returns a
:class:`~pyresample.geometry.SwathDefinition` describing the new ,
corrected geolocation. This ``SwathDefinition`` can then be used for
resampling a satpy Scene, yielding a corrected geolocation for all datasets
in the Scene. For example::

>>> global_scene = satpy.Scene(reader="seviri_l1b_hrit", filenames=files_sat)
>>> global_scene.load(['IR_087','IR_120'])
>>> global_nwc = satpy.Scene(filenames=files_nwc)
>>> global_nwc.load(['ctth'])
>>> area_def = satpy.resample.get_area_def(area)
>>> parallax_correction = ParallaxCorrection(area_def)
>>> plax_corr_area = forward_parallax(global_nwc["ctth"])
>>> local_scene = global_scene.resample(plax_corr_area)
>>> local_nwc = global_nwc.resample(plax_corr_area)
>>> local_scene[...].attrs["area"] = area_def
>>> local_nwc[...].attrs["area"] = area_def

Note that the ``ctth`` dataset must contain geolocation metadata, such as
set in the ``orbital_parameters`` dataset attribute by many readers.
"""

def __init__(self, base_area):
"""Initialise parallax correction class.

Args:
base_area (pyresample.AreaDefinition): Area for which calculated
geolocation will be calculated.
"""
self.base_area = base_area
self.source_area = None
self.resampler = None # we prepare this lazily

def __call__(self, cth_dataset):
"""Apply parallax correction to dataset.

Args:
cth_dataset: Dataset containing cloud top heights (or other heights
to be corrected).

Returns:
pyresample.geometry.SwathDefinition: Swathdefinition with corrected
lat/lon coordinates.
"""
return self.corrected_area(cth_dataset)

def corrected_area(self, cth_dataset):
"""Corrected area.

Calculate the corrected SwathDefinition for dataset.

Returns a parallax corrected swathdefinition of the base area.
"""
area = cth_dataset.area
(sat_lon, sat_lat, sat_alt) = get_satpos(cth_dataset)
cth_dataset = self._preprocess_cth(cth_dataset)
(pixel_lon, pixel_lat) = area.get_lonlats()

# Pixel coordinates according to parallax correction
(corr_lon, corr_lat) = forward_parallax(
sat_lon, sat_lat, sat_alt,
np.array(pixel_lon), np.array(pixel_lat), np.array(cth_dataset)
)

corr_lon = xr.DataArray(corr_lon)
corr_lat = xr.DataArray(corr_lat)
corr_area = SwathDefinition(corr_lon, corr_lat)

# But we are not actually moving pixels, rather we want a
# coordinate transformation. With this transformation we approximately
# invert the pixel coordinate transformation, giving the lon and lat
# where we should retrieve a value for a given pixel.
(proj_lon, proj_lat) = self._invert_lonlat(
pixel_lon, pixel_lat, corr_area)
proj_lon = xr.DataArray(proj_lon)
proj_lat = xr.DataArray(proj_lat)

return SwathDefinition(proj_lon, proj_lat)

def _preprocess_cth(self, cth_dataset):
"""To be documented."""
units = cth_dataset.units
cth = cth_dataset.copy().fillna(0.0)
if units == 'm': # convert to km
cth = cth * 1e-3
return cth

def _invert_lonlat(self, pixel_lon, pixel_lat, source_area):
gerritholl marked this conversation as resolved.
Show resolved Hide resolved
"""Invert the lon/lat coordinate transformation.

When a satellite observes a cloud, a reprojection onto the cloud has
already happened.
"""
(source_lon, source_lat) = source_area.get_lonlats()
grid_projection = projection.GridProjection(self.base_area)
(y, x) = grid_projection(source_lon, source_lat)
x = x.round().astype(int)
y = y.round().astype(int)

# set of points it starts with on an irregular "grid"
# needs to go from irregular "grid" to regular grid
# kernel density interpolation for regridding from irregular grid into
# to regular one
# radial basis interpolation
# calculates estimate of needed size of convolution kernel for density
# estimation
valid = (x >= 0) & (x < self.base_area.width) & \
(y >= 0) & (y < self.base_area.height)
num_in_area = np.count_nonzero(valid)
s = np.sqrt(np.prod(self.base_area.shape) / num_in_area) / 1.18
r = int(round(s*4))
(kx, ky) = np.mgrid[-r:r+1, -r:r+1]
k = np.exp(-0.5*(kx**2+ky**2)/s**2)
k /= k.sum()

# reevaluate for the expanded area
valid = ((x >= -r) & (x < self.base_area.width + r) &
(y >= -r) & (y < self.base_area.height + r))
x = x[valid] + r
y = y[valid]+r
pixel_lon = pixel_lon[valid]
pixel_lat = pixel_lat[valid]
expanded_shape = (self.base_area.shape[0]+2*r, self.base_area.shape[1]+2*r)

mask = np.zeros(expanded_shape)
mask[y, x] = 1
weight_sum = convolve(mask, k, mode='same')

def inv_coord(pixel_coord):
c = np.zeros(expanded_shape)
c[y, x] = pixel_coord
c = convolve(c, k, mode='same') / weight_sum
return c[r:-r, r:-r].copy().astype(np.float32)

inv_lon = inv_coord(pixel_lon)
inv_lat = inv_coord(pixel_lat)

return (inv_lon.astype(np.float32), inv_lat.astype(np.float32))