Source code for pylawr.grid.polar

#!/usr/bin/env python
# -*- coding: utf-8 -*-

# System modules
import logging
from collections import OrderedDict

# External modules
import numpy as np
import xarray as xr

# Internal modules
from pylawr.grid.base import BaseGrid
from pylawr.utilities.conventions import naming_convention


logger = logging.getLogger(__name__)


[docs]class PolarGrid(BaseGrid): """ The PolarGrid is the natural grid of a radar. The polar grid is defined such that the radar is the north pole of the grid. Within the initialization, the ranges and azimuth are valid for the lower left grid boundary. Later, the methods are valid for the centroid. Parameters ---------- center : tuple(float), optional The center of the grid. The center should have two or three entries within the tuple. The first entry is the latitude position of the grid center in degree. The second entry is the longitude position of the grid center in degree. The optional third entry is the height of the center above mean sea level in meters. If no third entry is given the center height is set to zero. The center is used to transform the polar coordinates into latitude and longitude coordinates. It is also used to calculate the altitude map together with the beam elevation. Default is the position on the top of the "Geomatikum": (53.56796, 9.97451, 95). beam_ele : float, optional The beam elevation in degrees. The beam elevation is used to calculate the altitude map. The beam elevation is set constant if it is a float. The value of the beam elevation are normalized for angle between -90 and 90 degrees such that the sign is preserved. This beam elevation is used to calculate the elevation for a grid point. The calculated elevation is valid for the whole grid box. nr_ranges : int, optional The number of ranges with every beam. Default is 333. range_res : float, optional The step width of every range in meters. Default is 59.95849. range_offset : float, optional The offset of the ranges in meters. This value is used as start value for the creation of the offsets. Default is 0. nr_azi : int, optional The number of azimuth angles. This is used to calculate the azimuth angles. Default is 360. azi_offset : float, optional The azimuth offset in degrees. This value is used as offset for the creation of the azimuth linspace. This could be used to rotate the radar grid. Default is 0. """ def __init__(self, center=(53.56796, 9.97451, 95), beam_ele=3, nr_ranges=333, range_res=59.95849, range_offset=0, nr_azi=360, azi_offset=0): super().__init__(center, beam_ele) self._coord_names = ('azimuth', 'range',) self._data_shape = (nr_azi, nr_ranges) self._align = None self._align_convert = OrderedDict( left=0, center=0.5, right=1, bottom=0, top=1 ) self.nr_ranges = nr_ranges self.range_res = range_res self.nr_azi = nr_azi self.range_offset = range_offset self.azi_offset = azi_offset @property def center_distance(self): return self.coord_fields[1] @property def beam_elevation_field(self): if isinstance(self._beam_elevation, np.ndarray): repeated_field = self._beam_elevation.repeat(self.nr_ranges) repeated_field = repeated_field.reshape(self._data_shape) return repeated_field else: return super().beam_elevation_field @property def ranges(self): return self.coords[1] @property def azimuth(self): return self.coords[0]
[docs] def _calc_coordinates(self): azi_centroid_off = 360 / self.nr_azi / 2 range_centroid_off = self.range_res / 2 azimuth = np.linspace(0, 360, self.nr_azi, endpoint=False, dtype=np.float64) azimuth += (self.azi_offset + azi_centroid_off) azimuth = azimuth % 360 range_space = np.arange(self.nr_ranges, dtype=np.float64) range_space *= self.range_res range_space += (self.range_offset + range_centroid_off) return azimuth, range_space
[docs] def _coords2latlon(self, azimuth, range): """ Method to convert the polar coordinate field of a radar to latitude and longitude coordinates. The formulas are taken from `Wikipedia-Nautisches-Dreieck <https://de.wikipedia.org/wiki/Nautisches_Dreieck>`_. Parameters ---------- range_field : Field containing the range values azimuth_field : Field containing the angle information in degree. Returns ------- latitude : numpy.ndarray The calculated latitude coordinate as numpy.ndarray with the shape of the coordinates. longitude : numpy.ndarray The calculated longitude coordinate as numpy.ndarray with the shape of the coordinates. """ lat_center = np.deg2rad(self.center[0]) lon_center = np.deg2rad(self.center[1]) a = np.deg2rad(-(180 + azimuth)) h = np.deg2rad(90) - range / self.earth_radius delta = np.arcsin(-np.cos(h) * np.cos(a) * np.cos(lat_center) + np.sin(h) * np.sin(lat_center) ) tau = np.arcsin(np.cos(h) * np.sin(a) / np.cos(delta)) latitude = np.rad2deg(delta) longitude = np.rad2deg(lon_center + tau) return latitude, longitude
[docs] def _coords_bounds(self): azimuth = np.linspace(0, 360, self.nr_azi+1, endpoint=True, dtype=np.float64) azimuth += self.azi_offset azimuth = azimuth % 360 range_space = np.arange(self.nr_ranges+1) * self.range_res range_space += self.range_offset return azimuth, range_space
[docs] def get_azimuth_offset(self): """ Get the azimuth offset for this grid as `xarray.DataArray`. Returns ------- elevation : `xarray.DataArray` The azimuth offset for this grid as xarray.DataArray in degrees. """ da_name = 'azimuth_offset' azimuth_offset = xr.DataArray( data=self.azi_offset, coords=None, dims=None, name=da_name, attrs=naming_convention[da_name] ) return azimuth_offset