Source code for pyart.map.polar_to_cartesian

"""
pyart.map.polar_to_cartesian
=========================================

Routines to project polar radar data to Cartesian coordinates

.. autosummary::
    :toctree: generated/

    polar_to_cartesian
    get_earth_radius
"""
from copy import deepcopy

import numpy as np
from scipy import spatial

KE = 4 / 3.  # Constant in the 4/3 earth radius model
# Two extreme earth radius
R_EARTH_MAX = 6378.1370 * 1000
R_EARTH_MIN = 6356.7523 * 1000


[docs]def polar_to_cartesian(radar_sweep, field_name, cart_res=75, max_range=None, mapping=None): """ Interpolates a PPI or RHI scan in polar coordinates to a regular cartesian grid of South-North and West-East coordinates (for PPI) or distance at ground and altitude coordinates (for RHI) Parameters ---------- radar : Radar Radar instance as generated py pyart sweep : int Sweep number to project to cartesian coordinates. field_name : str Name of the radar field to be interpolated cart_res : int, optional Resolution (in m.) of the cartesian grid to which polar data is interpolated max_range : int, optional Maximal allowed range (in m.) from radar for gates to be interpolated mapping : dict, optional Dictionnary of mapping indexes (from polar to cartesian), gets returned by the function (see below). Can be used as input when interpolating sequentially several variables for the same scan, to save significant time Returns ------- coords : tuple of 2 arrays 2D coordinates of the cartesian grid cart_data : 2D array Interpolated radar measurements (on the cartesian grid) mapping,: dict Dictionnary of mapping indexes (from polar to cartesian),which contains the indexes mapping the polar grid to the cartesian grid as well as some metadata. """ # Get data to be interpolated pol_data = radar_sweep.get_field(0, field_name) is_ppi = radar_sweep.sweep_mode['data'][0] == 'ppi' if mapping: # Check if mapping is usable: if is_ppi != mapping['is_ppi']: print('Input mapping does not correspond to given scan type, ignoring it') mapping = None elif mapping['dim_pol'] != pol_data.shape: print('Input mapping does not correspond to dimensions of given field' ', ignoring it') mapping = None else: cart_res = mapping['res'] max_range = mapping['max_range'] # Get distances of radar data r = radar_sweep.range['data'] if max_range is None: max_range = np.max(r) # Cut data at max_range pol_data_cut = deepcopy(pol_data[:, r < max_range]) r = r[r < max_range] # Set masked pixels to nan pol_data_cut.filled(np.nan) # One specificity of using the kd-tree is that we need to pad the array # with nans at large ranges and angles smaller and larger pol_data_cut = np.pad(pol_data_cut, pad_width=((1, 1), (0, 1)), mode='constant', constant_values=np.nan) # Get angles of radar data if is_ppi: theta = radar_sweep.azimuth['data'] else: theta = radar_sweep.elevation['data'] # We need to pad theta and r as well theta = np.hstack([np.min(theta) - 0.1, theta, np.max(theta) + 0.1]) r = np.hstack([r, np.max(r) + 0.1]) r_grid_p, theta_grid_p = np.meshgrid(r, theta) # Generate regular cartesian grid if is_ppi: x_vec = np.arange(-max_range - cart_res, max_range + cart_res, cart_res) y_vec = np.arange(-max_range - cart_res, max_range + cart_res, cart_res) else: x_vec = np.arange(min( [(max_range-cart_res)*np.cos(np.radians(np.max(theta))), 0]), max_range+cart_res, cart_res) y_vec = np.arange(0, max_range + cart_res, cart_res) x_grid_c, y_grid_c = np.meshgrid(x_vec, y_vec) if is_ppi: theta_grid_c = np.degrees(np.arctan2(-x_grid_c, -y_grid_c) + np.pi) r_grid_c = (np.sqrt(x_grid_c**2 + y_grid_c**2)) else: theta_grid_c = np.degrees(-(np.arctan2(x_grid_c, y_grid_c) - np.pi / 2)) E = get_earth_radius(radar_sweep.latitude['data']) r_grid_c = (np.sqrt((E * KE * np.sin(np.radians(theta_grid_c)))**2 + 2 * E * KE * y_grid_c + y_grid_c ** 2) - E * KE * np.sin(np.radians(theta_grid_c))) if not mapping: # Kd-tree construction and query kdtree = spatial.cKDTree(np.vstack((r_grid_p.ravel(), theta_grid_p.ravel())).T) _, mapping_idx = kdtree.query(np.vstack((r_grid_c.ravel(), theta_grid_c.ravel())).T, k=1) mapping = {'idx': mapping_idx, 'max_range': max_range, 'res': cart_res, 'is_ppi': is_ppi, 'dim_pol': pol_data.shape} cart_data = pol_data_cut.ravel()[mapping['idx']] cart_data = np.reshape(cart_data, x_grid_c.shape) return (x_vec, y_vec), cart_data, mapping
[docs]def get_earth_radius(latitude): """ Computes the earth radius for a given latitude Parameters ---------- latitude: latitude in degrees (WGS84) Returns ------- earth_radius : the radius of the earth at the given latitude """ a = R_EARTH_MAX b = R_EARTH_MIN num = ((a ** 2 * np.cos(latitude)) ** 2 + (b ** 2 * np.sin(latitude)) ** 2) den = ((a * np.cos(latitude)) ** 2 + (b * np.sin(latitude)) ** 2) earth_radius = np.sqrt(num / den) return earth_radius