Source code for hgrs.utils

# copyright 2025,  Magellium, J.-P. Burochin

import numpy as np
import xarray as xr

import xesmf as xe
import logging

[docs] class Reproj(): def __init__(self): pass
[docs] @staticmethod def regridding(input_dataset, output_grid_size=(1200, 1200), d_input_crs=4326, parallel=True): """ Take a PRISMA L1C product in sensor geometry (x,y) as input and return it in a georeferenced geometry (lon,lat). WARNING : Due to the use of the xESMF package, relying on Fortran, some user warnings like : "UserWarning: Input array is not F_CONTIGUOUS. Will affect performance." may be raised. It is not an issue in our case (see https://github.com/JiaweiZhuang/xESMF/issues/25). :param input_dataset: the product to regrid :param output_grid_size: (tuple) output grid size in (lon, lat) format :param d_input_crs: (int) code EPSG of the related geolocalisation frame :return output_dataset: the regularised product """ logging.info('georeferencing native image') # setting lon and lat as coordinates attrs = input_dataset.attrs #input_dataset = input_dataset.set_coords(["lon", "lat"]) # make the grid that the data will be regridded to grid_lons = np.linspace(input_dataset.lon.min().values, input_dataset.lon.max().values, output_grid_size[0]) grid_lats = np.linspace(input_dataset.lat.min().values, input_dataset.lat.max().values, output_grid_size[1]) new_grid = xr.Dataset({'lat': (['lat'], grid_lats), 'lon': (['lon'], grid_lons)}) new_grid = new_grid.chunk({"lat": 50, "lon": 50}) # use periodic=False if either or both the lat and lon dimensions are not regular regridder = xe.Regridder(input_dataset, new_grid, method='bilinear', periodic=False, unmapped_to_nan=True, parallel=parallel) # regrid the data output_dataset = regridder(input_dataset) # put the wavelength dependant data lost in the process, back in the dataset output_dataset = output_dataset.assign(fwhm=input_dataset.fwhm, F0=input_dataset.F0) # put "x","y" naming: output_dataset = output_dataset.rename({"lon": "x", "lat": "y"}) # adding the CRS output_dataset.rio.write_crs(d_input_crs, inplace=True) output_dataset.rio.set_spatial_dims(x_dim="x", y_dim="y", inplace=True) output_dataset.rio.write_coordinate_system(inplace=True) output_dataset.attrs.update(attrs) return output_dataset
[docs] class Misc: ''' Miscelaneous utilities '''
[docs] @staticmethod def get_pressure(alt, psl): '''Compute the pressure for a given altitude alt : altitude in meters (float or np.array) psl : pressure at sea level in hPa palt : pressure at the given altitude in hPa''' palt = psl * (1. - 0.0065 * np.nan_to_num(alt) / 288.15) ** 5.255 return palt
@staticmethod def transmittance_dir(aot, air_mass, rot=0): return np.exp(-(rot + aot) * air_mass) @staticmethod def air_mass(sza, vza): return 1 / np.cos(np.radians(vza)) + 1 / np.cos(np.radians(sza))
[docs] @staticmethod def earth_sun_correction(dayofyear): ''' Earth-Sun distance correction factor for adjustment of mean solar irradiance :param dayofyear: :return: correction factor ''' theta = 2. * np.pi * dayofyear / 365 d2 = 1.00011 + 0.034221 * np.cos(theta) + 0.00128 * np.sin(theta) + \ 0.000719 * np.cos(2 * theta) + 0.000077 * np.sin(2 * theta) return d2