Convert xarray to image tiles for WMS

I have an xarray dataset that I want to convert to image in for a WMS server I am creating in python. The problem is that my when I display my WMS with OpenLayer in my frontend, the tiles seems shifted.

I wonder if my reprojection and/or interpolation are the cause of this shift.
OpenLayers always request 256px by 256 px tiles .

My data have much more point that 256 in each axis, whatever the zoom level, so my interpolation is subsampling.

Her is my code:

def find_closest_indices(data, x_min, x_max, y_min, y_max):
    # Find the closest indices that are just outside the BBOX
    x_indices = [np.argmin(np.abs(data.x.values - x_min)),
                 np.argmin(np.abs(data.x.values - x_max))]
    y_indices = [np.argmin(np.abs(data.y.values - y_max)),
                 np.argmin(np.abs(data.y.values - y_min))]

    print(f"X indices: {x_indices}")
    print(f"Y indices: {y_indices}")

    # Extend indices to include the next point outside the BBOX
    x_indices = [max(0, x_indices[0] - 1),
                 min(len(data.x), x_indices[1] + 1)]
    y_indices = [max(0, y_indices[1] - 1),
                 min(len(data.y), y_indices[0] + 1)]

    return x_indices, y_indices


def extract_bbox(bbox_str: str) -> tuple:
    """Extract bounding box coordinates from a string"""
    return tuple(map(float, bbox_str.split(",")))


def transform_coordinates(proj_in, proj_out, lon_min, lat_min, lon_max, lat_max):
    """Transform bounding box coordinates to match dataset's coordinate system"""
    x_min, y_min = transform(proj_in, proj_out, lon_min, lat_min)
    x_max, y_max = transform(proj_in, proj_out, lon_max, lat_max)
    return x_min, y_min, x_max, y_max


def normalize_data(data: np.ndarray) -> np.ndarray:
    """Normalize data values to fit within a fixed range"""
    return (data - COLOR_SCALE_MIN) / (COLOR_SCALE_MAX - COLOR_SCALE_MIN)


# @lru_cache(maxsize=128)
def get_interpolated_data(filepath, var_name, bbox, width, height, crs, padding=1):
    """Extract and interpolate data from a NetCDF file"""
    lon_min, lat_min, lon_max, lat_max = bbox
    # Define projections (example UTM zone 33, adjust as needed)
    x_min, y_min, x_max, y_max = transform_coordinates(
        crs, "EPSG:32631", *bbox)

    # print(f"X min: {x_min}, X max: {x_max}")
    # print(f"Y min: {y_min}, Y max: {y_max}")

    ds = xr.open_dataset(filepath)
    data = ds[var_name]

    x_indices, y_indices = find_closest_indices(
        data, x_min, x_max, y_min, y_max)

    print(f"Data shape: {data.x.shape}, {data.y.shape}")

    # Select the first wavelength layer
    subset = data.isel(wl=2).isel(
        x=slice(x_indices[0], x_indices[1]),
        y=slice(y_indices[1], y_indices[0])
    )
    print(
        f"Subset shape: {subset.shape}, x: {subset.x.shape}, y: {subset.y.shape}")

    data_np = subset.values
    print(f"Data shape: {data_np.shape}")

    # Create a mask for NaN values
    nan_mask = np.isnan(data_np)
    print(f"NaN mask: {nan_mask.sum()} NaNs")

    # Apply NaN mask to keep NaNs in the data

    if data_np.shape == (0, 0) or data_np.shape[0] == 0 or data_np.shape[1] == 0:
        return data_np

    data_normalized = cv2.normalize(
        data_np, None, 0, 255, cv2.NORM_MINMAX).astype(np.uint8)
    data_normalized[nan_mask] = 0
    # Normalize data to a range suitable for OpenCV (e.g., uint8)
    # data_lon_min, data_lat_min, data_lon_max, data_lat_max = transform_coordinates(
    #     "EPSG:32631", crs, subset.x.min().item(), subset.y.min().item(), subset.x.max().item(), subset.y.max().item())

    # Convert bbox and data extent to pixel coordinates
    lon_range = x_max - x_min
    lat_range = x_max - x_min

    data_x_min = subset.x.min().item()
    data_y_min = subset.y.min().item()
    data_x_max = subset.x.max().item()
    data_y_max = subset.y.max().item()

    print(f"Data lon min: {data_x_min}, lon max: {data_x_max}")
    print(f"Data lat min: {data_y_min}, lat max: {data_y_max}")

    print(f"lon min: {x_min}, lon max: {x_max}")
    print(f"lat min: {y_min}, lat max: {y_max}")

    print((f"Range: {lon_range}, {lat_range}"))

    # Calculate the pixel coordinates of the data
    data_px_min = (data_x_min - x_min) / (x_max - x_min) * width
    data_py_min = (y_max - data_y_max) / (y_max - y_min) * height
    data_px_max = (data_x_max - x_min) / (x_max - x_min) * width
    data_py_max = (y_max - data_y_min) / (y_max - y_min) * height

    print("Before rounding")
    print(f"Data x min: {data_px_min}, x max: {data_px_max}")
    print(f"Data y min: {data_py_min}, y max: {data_py_max}")

    # Parse the pixel coordinates to integers
    data_px_min = int(data_px_min)
    data_py_min = int(data_py_min)
    data_px_max = int(data_px_max) + 1
    data_py_max = int(data_py_max) + 1

    if data_px_max > width:
        data_px_max = width
    if data_py_max > height:
        data_py_max = height
    if data_px_min < 0:
        data_px_min = 0
    if data_py_min < 0:
        data_py_min = 0

    print("After rounding")
    print(f"Data x min: {data_px_min}, x max: {data_px_max}")
    print(f"Data y min: {data_py_min}, y max: {data_py_max}")

    print(f"x_min: {x_min}, x_max: {x_max}")
    print(f"y_min: {y_min}, y_max: {y_max}")

    # Calculate the width and height of the data in pixel coordinates
    data_pixel_width = data_px_max - data_px_min
    data_pixel_height = data_py_max - data_py_min

    print(f"Data pixel width: {data_pixel_width}, height: {data_pixel_height}")

    data_zeros = np.zeros(
        (data_pixel_height, data_pixel_width), dtype=data_normalized.dtype)
    data

    # Resize the data to fit within its pixel coordinates
    data_resized = cv2.resize(
        data_normalized, (data_pixel_width, data_pixel_height), interpolation=cv2.INTER_AREA)

    print(f"Resized data shape: {data_resized.shape}")

    # Place the resized data into the target-sized array
    data_interpolated = np.zeros((height, width), dtype=data_resized.dtype)
    data_interpolated[data_py_min:data_py_max,
                      data_px_min:data_px_max] = data_resized

    data_interpolated = data_interpolated.astype(
        float)  # Convert to float to support NaN values
    data_interpolated[data_interpolated == 0] = np.nan

    return data_interpolated

def create_color_mapped_image(data, width, height, cmap_name='plasma'):
    """Create a color-mapped image from normalized data"""
    if data.size == 0:
        print("Data is empty")
        # Return a transparent image of default size
        return Image.new('RGBA', (256, 256), (255, 255, 255, 0))
    norm = Normalize(vmin=0, vmax=1)
    # print(f"Normalized data min: {norm.vmin}, max: {norm.vmax}")
    cmap = plt.get_cmap(cmap_name)
    mappable = ScalarMappable(norm=norm, cmap=cmap)
    mapped_data = (mappable.to_rgba(data, bytes=True)).astype(np.uint8)

    image = Image.fromarray(mapped_data, mode="RGBA")
    return image

Does anyone as experience in interpolation for tiles images ?

Trang chủ Giới thiệu Sinh nhật bé trai Sinh nhật bé gái Tổ chức sự kiện Biểu diễn giải trí Dịch vụ khác Trang trí tiệc cưới Tổ chức khai trương Tư vấn dịch vụ Thư viện ảnh Tin tức - sự kiện Liên hệ Chú hề sinh nhật Trang trí YEAR END PARTY công ty Trang trí tất niên cuối năm Trang trí tất niên xu hướng mới nhất Trang trí sinh nhật bé trai Hải Đăng Trang trí sinh nhật bé Khánh Vân Trang trí sinh nhật Bích Ngân Trang trí sinh nhật bé Thanh Trang Thuê ông già Noel phát quà Biểu diễn xiếc khỉ Xiếc quay đĩa Dịch vụ tổ chức sự kiện 5 sao Thông tin về chúng tôi Dịch vụ sinh nhật bé trai Dịch vụ sinh nhật bé gái Sự kiện trọn gói Các tiết mục giải trí Dịch vụ bổ trợ Tiệc cưới sang trọng Dịch vụ khai trương Tư vấn tổ chức sự kiện Hình ảnh sự kiện Cập nhật tin tức Liên hệ ngay Thuê chú hề chuyên nghiệp Tiệc tất niên cho công ty Trang trí tiệc cuối năm Tiệc tất niên độc đáo Sinh nhật bé Hải Đăng Sinh nhật đáng yêu bé Khánh Vân Sinh nhật sang trọng Bích Ngân Tiệc sinh nhật bé Thanh Trang Dịch vụ ông già Noel Xiếc thú vui nhộn Biểu diễn xiếc quay đĩa Dịch vụ tổ chức tiệc uy tín Khám phá dịch vụ của chúng tôi Tiệc sinh nhật cho bé trai Trang trí tiệc cho bé gái Gói sự kiện chuyên nghiệp Chương trình giải trí hấp dẫn Dịch vụ hỗ trợ sự kiện Trang trí tiệc cưới đẹp Khởi đầu thành công với khai trương Chuyên gia tư vấn sự kiện Xem ảnh các sự kiện đẹp Tin mới về sự kiện Kết nối với đội ngũ chuyên gia Chú hề vui nhộn cho tiệc sinh nhật Ý tưởng tiệc cuối năm Tất niên độc đáo Trang trí tiệc hiện đại Tổ chức sinh nhật cho Hải Đăng Sinh nhật độc quyền Khánh Vân Phong cách tiệc Bích Ngân Trang trí tiệc bé Thanh Trang Thuê dịch vụ ông già Noel chuyên nghiệp Xem xiếc khỉ đặc sắc Xiếc quay đĩa thú vị
Trang chủ Giới thiệu Sinh nhật bé trai Sinh nhật bé gái Tổ chức sự kiện Biểu diễn giải trí Dịch vụ khác Trang trí tiệc cưới Tổ chức khai trương Tư vấn dịch vụ Thư viện ảnh Tin tức - sự kiện Liên hệ Chú hề sinh nhật Trang trí YEAR END PARTY công ty Trang trí tất niên cuối năm Trang trí tất niên xu hướng mới nhất Trang trí sinh nhật bé trai Hải Đăng Trang trí sinh nhật bé Khánh Vân Trang trí sinh nhật Bích Ngân Trang trí sinh nhật bé Thanh Trang Thuê ông già Noel phát quà Biểu diễn xiếc khỉ Xiếc quay đĩa
Thiết kế website Thiết kế website Thiết kế website Cách kháng tài khoản quảng cáo Mua bán Fanpage Facebook Dịch vụ SEO Tổ chức sinh nhật