Source code for gcpy.grid_stretching_transforms

"""
Functions to apply grid-stretching transforms.
"""
import numpy as np


[docs] def rotate_vectors(x, y, z, k, theta): """ Rotates 3D vectors about an arbitrary axis using Rodrigues' rotation formula. Parameters ---------- x : array-like X-components of the vectors to be rotated. y : array-like Y-components of the vectors to be rotated. z : array-like Z-components of the vectors to be rotated. k : numpy.ndarray Unit vector defining the axis of rotation, shape (3,). theta : float Angle of rotation in radians. Returns ------- x : numpy.ndarray X-components of the rotated vectors. y : numpy.ndarray Y-components of the rotated vectors. z : numpy.ndarray Z-components of the rotated vectors. """ x = np.atleast_1d(x) y = np.atleast_1d(y) z = np.atleast_1d(z) v = np.moveaxis(np.array([x, y, z]), 0, -1) # shape: (..., 3) v = v * np.cos(theta) + np.cross(k, v) * np.sin(theta) + \ k[np.newaxis, :] * np.dot(v, k)[:, np.newaxis] * (1 - np.cos(theta)) return v[..., 0], v[..., 1], v[..., 2]
[docs] def cartesian_to_spherical(x, y, z): """ Converts Cartesian coordinates to spherical (longitude, latitude) coordinates in radians. Parameters ---------- x : array-like X-components of the Cartesian coordinates. y : array-like Y-components of the Cartesian coordinates. z : array-like Z-components of the Cartesian coordinates. Returns ------- x_sph : numpy.ndarray Longitude in radians, in the range [-pi, pi]. y_sph : numpy.ndarray Latitude in radians, in the range [-pi/2, pi/2]. """ x = np.atleast_1d(x) y = np.atleast_1d(y) z = np.atleast_1d(z) # Calculate x,y in spherical coordinates y_sph = np.arcsin(z) x_sph = np.arctan2(y, x) return x_sph, y_sph
[docs] def spherical_to_cartesian(x, y): """ Converts spherical (longitude, latitude) coordinates in radians to Cartesian coordinates. Parameters ---------- x : array-like Longitude in radians. y : array-like Latitude in radians. Returns ------- x_car : numpy.ndarray X-components of the Cartesian coordinates. y_car : numpy.ndarray Y-components of the Cartesian coordinates. z_car : numpy.ndarray Z-components of the Cartesian coordinates. """ x_car = np.cos(y) * np.cos(x) y_car = np.cos(y) * np.sin(x) z_car = np.sin(y) return x_car, y_car, z_car
[docs] def schmidt_transform(x, y, s): """ Applies the Schmidt transform to stretch a spherical grid toward a pole. Parameters ---------- x : array-like Longitude in radians. y : array-like Latitude in radians. s : float Stretch factor. Values greater than 1 compress the grid near the south pole and expand it near the north pole. Returns ------- x : array-like Longitude in radians (unchanged by this transform). y : numpy.ndarray Stretched latitude in radians. Notes ----- The Schmidt parameter D is computed as (1 - s**2) / (1 + s**2). """ D = (1 - s ** 2) / (1 + s ** 2) y = np.arcsin((D + np.sin(y)) / (1 + D * np.sin(y))) return x, y
[docs] def scs_transform(x, y, s, tx, ty): """ Applies the Stretched Cubed-Sphere (SCS) transform to a longitude/latitude grid. This combines a Schmidt stretch with rotations about the x- and z-axes to relocate the high-resolution region to a target longitude and latitude. Parameters ---------- x : array-like Longitude in degrees. y : array-like Latitude in degrees. s : float Stretch factor. Values greater than 1 increase the resolution near the target point. tx : float Target longitude in degrees. The center of the high-resolution region will be placed at this longitude. ty : float Target latitude in degrees. The center of the high-resolution region will be placed at this latitude. Returns ------- x : numpy.ndarray Transformed longitude in degrees. y : numpy.ndarray Transformed latitude in degrees. Notes ----- The transform proceeds in the following steps: 1. Convert all angles from degrees to radians. 2. Apply the Schmidt transform to stretch the grid. 3. Convert to Cartesian coordinates. 4. Rotate about the y-axis by (ty - y0) to shift the target latitude. 5. Rotate about the z-axis by (tx - x0) to shift the target longitude. 6. Convert back to spherical coordinates and then to degrees. """ # Convert xy to radians x = x * np.pi / 180 y = y * np.pi / 180 tx = tx * np.pi / 180 ty = ty * np.pi / 180 # Calculate rotation about x, and z axes x0 = np.pi y0 = -np.pi / 2 theta_x = ty - y0 theta_z = tx - x0 # Apply schmidt transform x, y = schmidt_transform(x, y, s) # Convert to cartesian coordinates x, y, z = spherical_to_cartesian(x, y) # Rotate about x axis xaxis = np.array([0, 1, 0]) x, y, z = rotate_vectors(x, y, z, xaxis, theta_x) # Rotate about z axis zaxis = np.array([0, 0, 1]) x, y, z = rotate_vectors(x, y, z, zaxis, theta_z) # Convert back to spherical coordinates x, y = cartesian_to_spherical(x, y, z) # Convert back to degrees and return x = x * 180 / np.pi y = y * 180 / np.pi return x, y