Example #1
0
def add_textures(radar,
                 fields=None,
                 gatefilter=None,
                 texture_window=(3, 3),
                 texture_sample=5,
                 min_ncp=None,
                 min_sweep=None,
                 max_sweep=None,
                 min_range=None,
                 max_range=None,
                 rays_wrap_around=False,
                 fill_value=None,
                 ncp_field=None,
                 debug=False,
                 verbose=False):
    """
    """

    # Parse fill value
    if fill_value is None:
        fill_value = get_fillvalue()

    # Parse field names
    if ncp_field is None:
        ncp_field = get_field_name('normalized_coherent_power')

    # Parse fields to compute textures
    # If no fields are specified then the texture field of all available radar
    # fields are computed
    if fields is None:
        fields = radar.fields.keys()

    # Parse texture window parameters
    ray_window, gate_window = texture_window

    if debug:
        print 'Number of rays in window: {}'.format(ray_window)
        print 'Number of gates in window: {}'.format(gate_window)

    for field in fields:
        if verbose:
            print 'Computing texture field: {}'.format(field)

        _compute_field(radar,
                       field,
                       gatefilter=gatefilter,
                       ray_window=ray_window,
                       gate_window=gate_window,
                       min_sample=texture_sample,
                       min_ncp=min_ncp,
                       min_sweep=min_sweep,
                       max_sweep=max_sweep,
                       min_range=min_range,
                       max_range=max_range,
                       rays_wrap_around=rays_wrap_around,
                       fill_value=fill_value,
                       text_field=None,
                       ncp_field=ncp_field)

    return
Example #2
0
def _make_constant_refl_radar(fill=5.0):
    """ Create radar with constant reflectivity. """

    radar = sample_objects.make_empty_ppi_radar(101, 360, 1)
    refl_dict = get_metadata('reflectivity')
    refl_dict['data'] = np.full((radar.nrays, radar.ngates), fill)
    radar.add_field(get_field_name('reflectivity'), refl_dict)
    return radar
Example #3
0
def hzt2radar_coord(radar, hzt_coord, slice_xy=True, field_name=None):
    """
    Given the radar coordinates find the nearest HZT pixel

    Parameters
    ----------
    radar : Radar
        the radar object containing the information on the position of the
        radar gates
    hzt_coord : dict
        dictionary containing the HZT coordinates
    slice_xy : boolean
        if true the horizontal plane of the HZT field is cut to the
        dimensions of the radar field
    field_name : str
        name of the field

    Returns
    -------
    hzt_ind_field : dict
        dictionary containing a field of HZT indices and metadata

    """
    # parse the field parameters
    if field_name is None:
        field_name = get_field_name('hzt_index')

    x_radar, y_radar, _ = _put_radar_in_swiss_coord(radar)

    x_hzt, y_hzt, ind_xmin, ind_ymin, ind_xmax, _ = (
        _prepare_for_interpolation(x_radar,
                                   y_radar,
                                   hzt_coord,
                                   slice_xy=slice_xy))

    tree = cKDTree(np.transpose((y_hzt, x_hzt)))
    _, ind_vec = tree.query(np.transpose(
        (y_radar.flatten(), x_radar.flatten())),
                            k=1)

    # put the index in the original cosmo coordinates
    nx_hzt = len(hzt_coord['x']['data'])

    nx = ind_xmax - ind_xmin + 1

    ind_y = (ind_vec / nx).astype(int) + ind_ymin
    ind_x = (ind_vec % nx).astype(int) + ind_xmin
    ind_hzt = (ind_x + nx_hzt * ind_y).astype(int)

    hzt_ind_field = get_metadata(field_name)
    hzt_ind_field['data'] = ind_hzt.reshape(radar.nrays, radar.ngates)

    return hzt_ind_field
Example #4
0
def _make_real_psidp_radar():
    """
    Create single-ray radar with linear differential phase profile with
    specified slope.
    ---
    Returns
    -------
    radar : Radar
            PyART radar instance with differential phase profile in deg.
    """
    psidp = np.array([[
        -2.33313751e+00, 1.80617523e+00, 7.17742920e-01, 1.82811661e+01,
        1.89352417e+01, 1.67904205e+01
    ]])
    psidp = np.ma.array(psidp)
    radar = sample_objects.make_empty_ppi_radar(len(psidp[0]), 1, 1)
    psidp_dict = {
        'data': psidp,
    }
    radar.add_field(get_field_name('differential_phase'), psidp_dict)
    # Define real ranges
    radar.range['data'] = 75 * np.arange(0, len(psidp[0]))
    return radar
Example #5
0
def _make_linear_psidp_radar(slope=0.002):
    """
    Create single-ray radar with linear differential phase profile with
    specified slope.

    Parameters
    ----------
    slope : float, optional
        Slope of differential phase profile in deg/m. Radar range gates cover
        0-1000 m, inclusive, with 10 m gate spacings.

    Returns
    -------
    radar : Radar
        Radar with linear differential phase profile in deg.

    """
    radar = sample_objects.make_empty_ppi_radar(101, 1, 1)
    psidp_dict = {
        'data': np.atleast_2d(np.linspace(0.0, slope * 1000.0, radar.ngates))
        }
    radar.add_field(get_field_name('differential_phase'), psidp_dict)

    return radar
Example #6
0
def velocity_phasor_coherency(radar,
                              gatefilter=None,
                              text_bins=40,
                              text_limits=(0, 20),
                              nyquist=None,
                              texture_window=(3, 3),
                              texture_sample=5,
                              max_texture=None,
                              rays_wrap_around=False,
                              remove_small_features=False,
                              size_bins=75,
                              size_limits=(0, 300),
                              fill_value=None,
                              vdop_field=None,
                              phasor_field=None,
                              text_field=None,
                              coherent_field=None,
                              debug=False,
                              verbose=False):
    """
    """

    # Parse fill value
    if fill_value is None:
        fill_value = get_fillvalue()

    # Parse field names
    if vdop_field is None:
        vdop_field = get_field_name('velocity')
    if phasor_field is None:
        phasor_field = '{}_phasor_real'.format(vdop_field)
    if text_field is None:
        text_field = '{}_texture'.format(phasor_field)
    if coherent_field is None:
        coherent_field = '{}_coherency_mask'.format(phasor_field)

    if verbose:
        print 'Computing Doppler velocity phasor coherency mask'

    # Parse Nyquist velocity
    if nyquist is None:
        nyquist = radar.get_nyquist_vel(0, check_uniform=True)

    if debug:
        print 'Radar Nyquist velocity: {:.3f} m/s'.format(nyquist)

    # Compute the real part of Doppler velocity phasor
    # Normalize real part of phasor to the Nyquist interval
    vdop = radar.fields[vdop_field]['data']
    phasor_real = nyquist * np.cos(np.radians(360.0 * vdop / nyquist))

    # Mask invalid values
    phasor_real = np.ma.masked_invalid(phasor_real)
    phasor_real.set_fill_value(fill_value)

    # Create Doppler velocity phasor field dictionary
    phasor_dict = {
        'data':
        phasor_real.astype(np.float32),
        'long_name':
        'Real part of Doppler velocity phasor',
        'standard_name':
        phasor_field,
        'valid_min':
        -nyquist,
        'valid_max':
        nyquist,
        '_FillValue':
        phasor_real.fill_value,
        'units':
        'meters_per_second',
        'comment': ('Real part of Doppler velocity phasor normalized to the '
                    'Nyquist interval'),
    }
    radar.add_field(phasor_field, phasor_dict, replace_existing=True)

    # Compute Doppler velocity phasor texture field
    ray_window, gate_window = texture_window
    texture_fields._compute_field(radar,
                                  phasor_field,
                                  ray_window=ray_window,
                                  gate_window=gate_window,
                                  min_sample=texture_sample,
                                  min_ncp=None,
                                  min_sweep=None,
                                  max_sweep=None,
                                  fill_value=fill_value,
                                  ncp_field=None)

    # Automatically bracket coherent part of Doppler velocity phasor texture
    # distribution
    if max_texture is None:

        # Bin Doppler velocity phasor texture data and count occurrences
        # Compute bin centers and bin width
        counts, bin_edges = np.histogram(
            radar.fields[text_field]['data'].compressed(),
            bins=text_bins,
            range=text_limits,
            normed=False,
            weights=None,
            density=False)
        bin_centers = bin_edges[:-1] + np.diff(bin_edges) / 2.0
        bin_width = np.diff(bin_edges).mean()

        if debug:
            print 'Bin width: {:.3f} m/s'.format(bin_width)

        # Determine positions of the extrema in the Doppler velocity phasor
        # texture distribution
        kmin = argrelextrema(counts, np.less, order=1, mode='clip')[0]
        kmax = argrelextrema(counts, np.greater, order=1, mode='clip')[0]

        if debug:
            print 'Minima located at: {} m/s'.format(bin_centers[kmin])
            print 'Maxima located at: {} m/s'.format(bin_centers[kmax])

        # Compute the theoretical noise peak location from Guassian noise
        # statistics
        noise_peak_theory = 2.0 * nyquist / np.sqrt(12.0)

        if debug:
            print 'Theoretical noise peak: {:.3f} m/s'.format(
                noise_peak_theory)

        # Find the closest Doppler velocity phasor texture distribution peak to
        # the computed theoretical location
        # Here we assume that the Doppler velocity phasor texture distribution
        # has at least one primary mode which correspondes to the incoherent
        # (noisy) part of the Doppler velocity phasor texture distribution
        # Depending on the radar volume and the bin width used to define the
        # distribution, the distribution may be bimodal, with the new peak
        # corresponding to the coherent part of the Doppler velocity phasor
        # texture distribution
        idx = np.abs(bin_centers[kmax] - noise_peak_theory).argmin()
        noise_peak = bin_centers[kmax][idx]

        if debug:
            print 'Computed noise peak: {:.3f} m/s'.format(noise_peak)

        # Determine primary and secondary peak locations for debugging
        # purposes
        if kmax.size > 1:
            counts_max = np.sort(counts[kmax], kind='mergesort')[::-1]
            prm_peak = bin_centers[np.abs(counts - counts_max[0]).argmin()]
            sec_peak = bin_centers[np.abs(counts - counts_max[1]).argmin()]

            if debug:
                print 'Primary peak: {:.3f} m/s'.format(prm_peak)
                print 'Secondary peak: {:.3f} m/s'.format(sec_peak)

        # Determine the left edge of the noise distribution
        # Where this distribution becomes a minimum will define the separation
        # between coherent and incoherent Doppler velocity phasor values
        is_left_side = bin_centers[kmin] < noise_peak
        max_texture = bin_centers[kmin][is_left_side].max() + bin_width / 2.0

        if debug:
            _range = [0.0, round(max_texture, 3)]
            print 'Doppler velocity phasor coherency mode: {} m/s'.format(
                _range)

    # Create Doppler velocity phasor coherency mask
    is_coherent = np.logical_and(
        radar.fields[text_field]['data'] >= 0.0,
        radar.fields[text_field]['data'] <= max_texture)
    is_coherent = np.ma.filled(is_coherent, False)

    # Create Doppler velocity phasor coherency mask dictionary
    coherent_dict = {
        'data': is_coherent.astype(np.int8),
        'long_name': 'Doppler velocity phasor coherency',
        'standard_name': coherent_field,
        'valid_min': 0,
        'valid_max': 1,
        '_FillValue': None,
        'units': 'unitless',
        'comment': '0 = incoherent value, 1 = coherent value',
    }
    radar.add_field(coherent_field, coherent_dict, replace_existing=True)

    # Remove insignificant features from Doppler velocity phasor coherency mask
    if remove_small_features:
        basic_fixes._binary_significant_features(radar,
                                                 coherent_field,
                                                 size_bins=size_bins,
                                                 size_limits=size_limits,
                                                 structure=structure,
                                                 debug=debug,
                                                 verbose=verbose)

    # Parse gate filter
    if gatefilter is None:
        gatefilter = GateFilter(radar, exclude_based=False)

    # Update gate filter
    gatefilter.include_equal(coherent_field, 1, op='and')

    return gatefilter
Example #7
0
def significant_detection(radar,
                          gatefilter=None,
                          remove_small_features=True,
                          size_bins=75,
                          size_limits=(0, 300),
                          fill_holes=False,
                          dilate=False,
                          structure=None,
                          iterations=1,
                          rays_wrap_around=False,
                          min_ncp=None,
                          ncp_field=None,
                          detect_field=None,
                          debug=False,
                          verbose=False):
    """
    Determine the significant detection of a radar. Note that significant
    detection can still include other non-meteorological echoes that the user
    may still have to remove further down the processing chain.

    Parameters
    ----------
    radar : Radar
        Radar object used to determine the appropriate GateFilter.
    gatefilter : GateFilter, optional
        If None, all radar gates will initially be assumed valid.
    remove_small_features : bool, optional
        True to remove insignificant echo features (e.g., salt and pepper
        noise) from significant detection mask.
    size_bins : int, optional
        Number of bins used to bin echo feature sizes and thus define its
        distribution.
    size_limits : list or tuple, optional
        Limits of the echo feature size distribution. The upper limit needs to
        be large enough to include the minimum feature size.
    fill_holes : bool, optional
        Fill any holes in the significant detection mask. For most radar
        volumes this should not be used since the default structuring element
        will automatically fill any sized hole.
    dilate : bool, optional
        Use binary dilation to fill in edges of the significant detection mask.
    structure : array_like, optional
        The binary structuring element used for all morphology routines. See
        SciPy's ndimage documentation for more information.
    iterations : int, optional
        The number of iterations to repeat binary dilation. If iterations is
        less than 1, binary dilation is repeated until the result does not
        change anymore.
    rays_wrap_around : bool, optional
        Whether the rays at the beginning and end of a sweep are connected
        (e.g., PPI VCP).
    min_ncp : float, optional
        Minimum normalized coherent power (signal quality) value used to
        indicate a significant echo.
    ncp_field : str, optional
        Minimum normalized coherent power (signal quality) field name. The
        default uses the Py-ART configuation file.
    detect_field : str, optional
        Radar significant detection mask field name.
    debug : bool, optional
        True to print debugging information, False to suppress.
    verbose : bool, optional
        True to print progress information, False to suppress.

    Returns
    -------
    gatefilter : GateFilter
        Py-ART GateFilter object indicating which radar gates are valid and
        invalid.
    """

    # Parse field names
    if ncp_field is None:
        ncp_field = get_field_name('normalized_coherent_power')
    if detect_field is None:
        detect_field = 'significant_detection_mask'

    # Parse gate filter
    if gatefilter is None:
        gatefilter = GateFilter(radar, exclude_based=False)

    # Exclude gates with poor signal quality
    if min_ncp is not None and ncp_field in radar.fields:
        gatefilter.include_above(ncp_field, min_ncp, op='and', inclusive=True)

    detect_dict = {
        'data': gatefilter.gate_included.astype(np.int8),
        'long_name': 'Radar significant detection mask',
        'standard_name': 'significant_detection_mask',
        'valid_min': 0,
        'valid_max': 1,
        '_FillValue': None,
        'units': 'unitless',
        'comment': '0 = no significant detection, 1 = significant detection',
    }
    radar.add_field(detect_field, detect_dict, replace_existing=True)

    # Remove insignificant features from significant detection mask
    if remove_small_features:
        basic_fixes._binary_significant_features(radar,
                                                 detect_field,
                                                 size_bins=size_bins,
                                                 size_limits=size_limits,
                                                 structure=structure,
                                                 debug=debug,
                                                 verbose=verbose)

    # Fill holes in significant detection mask
    if fill_holes:
        basic_fixes._binary_fill(radar, detect_field, structure=structure)

    # Dilate significant detection mask
    if dilate:
        basic_fixes._binary_dilation(radar,
                                     detect_field,
                                     structure=structure,
                                     iterations=iterations,
                                     debug=debug,
                                     verbose=verbose)

    # Update gate filter
    gatefilter.include_equal(detect_field, 1, op='new')

    return gatefilter
from matplotlib.colorbar import make_axes
from matplotlib.ticker import MultipleLocator

from pyart.io import read
from pyart.graph import cm
from pyart.config import get_field_name
from pyart.util.datetime_utils import datetimes_from_radar

# Define sweeps to plot
SWEEPS = [0, 2, 4]

# Define max range to plot
MAX_RANGE = 100.0

# Define field names
REFL_FIELD = get_field_name('reflectivity')
VDOP_FIELD = get_field_name('velocity')
SPW_FIELD = get_field_name('spectrum_width')

# Define colour maps
CMAP_REFL = cm.NWSRef
CMAP_VDOP = cm.NWSVel
CMAP_SPW = cm.NWS_SPW

# Normalize colour maps
NORM_REFL = BoundaryNorm(np.arange(-10, 65, 5), CMAP_REFL.N)
NORM_VDOP = BoundaryNorm(np.arange(-30, 32, 2), CMAP_VDOP.N)
NORM_SPW = BoundaryNorm(np.arange(0, 8.5, 0.5), CMAP_SPW.N)

# Define colour bar ticks
TICKS_REFL = np.arange(-10, 70, 10)
Example #9
0
    def __init__(self, Vradar=None, Vgrid=None, name="Mapper", parent=None):
        '''Initialize the class to create the interface.

        Parameters
        ----------
        [Optional]
        Vradar : :py:class:`~artview.core.core.Variable` instance
            Radar signal variable.
            A value of None initializes an empty Variable.
        Vgrid : :py:class:`~artview.core.core.Variable` instance
            Grid signal variable.
            A value of None initializes an empty Variable.
        name : string
            Field Radiobutton window name.
        parent : PyQt instance
            Parent instance to associate to this class.
            If None, then Qt owns, otherwise associated w/ parent PyQt instance
        '''
        super(Mapper, self).__init__(name=name, parent=parent)
        self.central_widget = QtWidgets.QWidget()
        self.setCentralWidget(self.central_widget)
        self.layout = QtWidgets.QGridLayout(self.central_widget)

        self.mountUI()

        self.parameters = {
            "radars": None,
            "gridshape": (1, 500, 500),
            "grid_limits": (
                (2000, 3000),
                (-250000, 250000),
                (-250000, 250000)),
            "grid_origin": (0, 0),
            "grid_origin_lat": 0,
            "grid_origin_lon": 0,
            "grid_origin_alt": 0,
            "gridding_algo": 'map_gates_to_grid',
            "fields": [],
            "refl_filter_flag": True,
            "refl_field": get_field_name('reflectivity'),
            "max_refl": 100,
            "map_roi": True,
            "weighting_function": "Barnes",
            "toa": 17000,
            "roi_func": "dist_beam",
            "constant_roi": 500,
            "z_factor": 0.05,
            "xy_factor": 0.02,
            "min_radius": 500,
            "bsp": 1,
            "copy_field_data": True,
            "algorithm": "kd_tree",
            "leafsize": 10,
            }

        self.general_parameters_type = [
            ("grid_origin_lat", float),
            ("grid_origin_lon", float),
            ("grid_origin_alt", float),
            ("gridding_algo", ('map_to_grid', 'map_gates_to_grid')),
            ("refl_filter_flag", bool),
            ("refl_field", str),
            ("max_refl", float),
            ("map_roi", bool),
            ("weighting_function", ("Barnes", "Cressman")),
            ("toa", float),
            ]
        self.roi_parameters_type = [
            ("roi_func", ("constant", "dist", "dist_beam")),
            ("constant_roi", float),
            ("z_factor", float),
            ("xy_factor", float),
            ("min_radius", float),
            ("bsp", float)
            ]
        self.gridding_parameters_type = [
            ("copy_field_data", bool),
            ("algorithm", ("kd_tree", "ball_tree")),
            ("leafsize", int),
            ]

        if Vradar is None:
            self.Vradar = Variable(None)
        else:
            self.Vradar = Vradar

        if Vgrid is None:
            self.Vgrid = Variable(None)
        else:
            self.Vgrid = Vgrid
        self.sharedVariables = {"Vradar": self.NewRadar,
                                "Vgrid": None}
        self.connectAllVariables()

        self.NewRadar(None, True)
        self.show()
Example #10
0
def cosmo2radar_coord(radar, cosmo_coord, slice_xy=True, slice_z=False,
                      field_name=None):
    """
    Given the radar coordinates find the nearest COSMO model pixel

    Parameters
    ----------
    radar : Radar
        the radar object containing the information on the position of the
        radar gates
    cosmo_coord : dict
        dictionary containing the COSMO coordinates
    slice_xy : boolean
        if true the horizontal plane of the COSMO field is cut to the
        dimensions of the radar field
    slice_z : boolean
        if true the vertical plane of the COSMO field is cut to the dimensions
        of the radar field
    field_name : str
        name of the field

    Returns
    -------
    cosmo_ind_field : dict
        dictionary containing a field of COSMO indices and metadata

    """
    # debugging
    # start_time = time.time()

    # parse the field parameters
    if field_name is None:
        field_name = get_field_name('cosmo_index')

    x_radar, y_radar, z_radar = _put_radar_in_swiss_coord(radar)

    (x_cosmo, y_cosmo, z_cosmo, ind_xmin, ind_ymin, ind_zmin, ind_xmax,
     ind_ymax, _) = _prepare_for_interpolation(
         x_radar, y_radar, z_radar, cosmo_coord, slice_xy=slice_xy,
         slice_z=slice_z)

    print('Generating tree')
    # default scipy compact_nodes and balanced_tree = True
    tree = cKDTree(
        np.transpose((z_cosmo, y_cosmo, x_cosmo)), compact_nodes=False,
        balanced_tree=False)
    print('Tree generated')
    _, ind_vec = tree.query(np.transpose(
        (z_radar.flatten(), y_radar.flatten(), x_radar.flatten())), k=1)

    # put the index in the original cosmo coordinates
    nx_cosmo = len(cosmo_coord['x']['data'])
    ny_cosmo = len(cosmo_coord['y']['data'])

    nx = ind_xmax-ind_xmin+1
    ny = ind_ymax-ind_ymin+1

    ind_z = (ind_vec/(nx*ny)).astype(int)+ind_zmin
    ind_y = ((ind_vec-nx*ny*ind_z)/nx).astype(int)+ind_ymin
    ind_x = ((ind_vec-nx*ny*ind_z) % nx).astype(int)+ind_xmin
    ind_cosmo = (ind_x+nx_cosmo*ind_y+nx_cosmo*ny_cosmo*ind_z).astype(int)

    cosmo_ind_field = get_metadata(field_name)
    cosmo_ind_field['data'] = ind_cosmo.reshape(radar.nrays, radar.ngates)

    # debugging
    # print(" generating COSMO indices takes %s seconds " %
    #      (time.time() - start_time))

    return cosmo_ind_field
    def __init__(self,
                 Vradar=None,
                 Vgatefilter=None,
                 name="DealiasUnwrapPhase",
                 parent=None):
        '''Initialize the class to create the interface.

        Parameters
        ----------
        [Optional]
        Vradar : :py:class:`~artview.core.core.Variable` instance
            Radar signal variable.
            A value of None initializes an empty Variable.
        name : string
            Field Radiobutton window name.
        parent : PyQt instance
            Parent instance to associate to this class.
            If None, then Qt owns, otherwise associated w/ parent PyQt instance
        '''
        super(DealiasUnwrapPhase, self).__init__(name=name, parent=parent)
        self.central_widget = QtWidgets.QWidget()
        self.setCentralWidget(self.central_widget)
        self.layout = QtWidgets.QGridLayout(self.central_widget)

        self.despeckleButton = QtWidgets.QPushButton("DealiasUnwrapPhase")
        self.despeckleButton.clicked.connect(self.dealias_unwrap_phase)
        self.layout.addWidget(self.despeckleButton, 0, 0)

        parentdir = os.path.abspath(
            os.path.join(os.path.dirname(__file__), os.pardir))
        config_icon = QtGui.QIcon(
            os.sep.join([
                parentdir, 'icons', "categories-applications-system-icon.png"
            ]))
        self.configButton = QtWidgets.QPushButton(config_icon, "")
        self.layout.addWidget(self.configButton, 0, 1)
        self.configMenu = QtWidgets.QMenu(self)
        self.configButton.setMenu(self.configMenu)

        self.configMenu.addAction(
            QtWidgets.QAction("Set Parameters",
                              self,
                              triggered=self.setParameters))
        self.configMenu.addAction(
            QtWidgets.QAction("Help", self, triggered=self._displayHelp))
        self.parameters = {
            "radar": None,
            "gatefilter": None,
            "unwrap_unit": "sweep",
            "interval_limits": None,
            "nyquist_velocity": "",
            "check_nyquist_uniform": False,
            "rays_wrap_around": True,
            "keep_original": False,
            "vel_field": get_field_name('velocity'),
            "corr_vel_field": get_field_name('corrected_velocity'),
            "skip_checks": False
        }

        self.parameters_type = [("unwrap_unit", ('ray', 'sweep', 'volume')),
                                ("nyquist_velocity", float,
                                 common.float_or_none),
                                ("check_nyquist_uniform", bool),
                                ("rays_wrap_around", bool),
                                ("keep_original", bool), ("vel_field", str),
                                ("corr_vel_field", str), ("skip_checks", bool)]

        self.layout.setColumnStretch(0, 1)

        if Vradar is None:
            self.Vradar = Variable(None)
        else:
            self.Vradar = Vradar

        if Vgatefilter is None:
            self.Vgatefilter = Variable(None)
        else:
            self.Vgatefilter = Vgatefilter

        self.sharedVariables = {"Vradar": None, "Vgatefilter": None}
        self.connectAllVariables()

        self.show()
Example #12
0
from matplotlib import rcParams
from matplotlib.colors import BoundaryNorm
from matplotlib.colorbar import make_axes
from matplotlib.ticker import MultipleLocator

from pyart.graph import cm
from pyart.io import read
from pyart.config import get_field_name
from pyart.util.datetime_utils import datetimes_from_radar

# Define sweeps to be plotted
SWEEPS = [0, 1, 2]

# Define field names
REFL_FIELD = get_field_name("reflectivity")
VDOP_FIELD = get_field_name("velocity")
SPW_FIELD = get_field_name("spectrum_width")
RHOHV_FIELD = get_field_name("cross_correlation_ratio")
ZDR_FIELD = get_field_name("differential_reflectivity")
PHIDP_FIELD = get_field_name("differential_phase")
NCP_FIELD = get_field_name("normalized_coherent_power")

# Define fields to exclude from radar object
EXCLUDE_FIELDS = ["corrected_reflectivity", "radar_echo_classification", "corrected_differential_reflectivity"]

# Define colour maps
CMAP_REFL = cm.NWSRef
CMAP_VDOP = cm.NWSVel
CMAP_SPW = cm.NWS_SPW
CMAP_RHOHV = cm.Carbone17
Example #13
0
    def __init__(self, Vradar=None, Vgrid=None, name="Mapper", parent=None):
        '''Initialize the class to create the interface.

        Parameters
        ----------
        [Optional]
        Vradar : :py:class:`~artview.core.core.Variable` instance
            Radar signal variable.
            A value of None initializes an empty Variable.
        Vgrid : :py:class:`~artview.core.core.Variable` instance
            Grid signal variable.
            A value of None initializes an empty Variable.
        name : string
            Field Radiobutton window name.
        parent : PyQt instance
            Parent instance to associate to this class.
            If None, then Qt owns, otherwise associated w/ parent PyQt instance
        '''
        super(Mapper, self).__init__(name=name, parent=parent)
        self.central_widget = QtWidgets.QWidget()
        self.setCentralWidget(self.central_widget)
        self.layout = QtWidgets.QGridLayout(self.central_widget)

        self.mountUI()

        self.parameters = {
            "radars": None,
            "gridshape": (1, 500, 500),
            "grid_limits": (
                (2000, 3000),
                (-250000, 250000),
                (-250000, 250000)),
            "grid_origin": (0, 0),
            "grid_origin_lat": 0,
            "grid_origin_lon": 0,
            "grid_origin_alt": 0,
            "gridding_algo": 'map_gates_to_grid',
            "fields": [],
            "refl_filter_flag": True,
            "refl_field": get_field_name('reflectivity'),
            "max_refl": 100,
            "map_roi": True,
            "weighting_function": "Barnes",
            "toa": 17000,
            "roi_func": "dist_beam",
            "constant_roi": 500,
            "z_factor": 0.05,
            "xy_factor": 0.02,
            "min_radius": 500,
            "bsp": 1,
            "copy_field_data": True,
            "algorithm": "kd_tree",
            "leafsize": 10,
            }

        self.general_parameters_type = [
            ("grid_origin_lat", float),
            ("grid_origin_lon", float),
            ("grid_origin_alt", float),
            ("gridding_algo", ('map_to_grid', 'map_gates_to_grid')),
            ("refl_filter_flag", bool),
            ("refl_field", str),
            ("max_refl", float),
            ("map_roi", bool),
            ("weighting_function", ("Barnes", "Cressman")),
            ("toa", float),
            ]
        self.roi_parameters_type = [
            ("roi_func", ("constant", "dist", "dist_beam")),
            ("constant_roi", float),
            ("z_factor", float),
            ("xy_factor", float),
            ("min_radius", float),
            ("bsp", float)
            ]
        self.gridding_parameters_type = [
            ("copy_field_data", bool),
            ("algorithm", ("kd_tree", "ball_tree")),
            ("leafsize", int),
            ]

        if Vradar is None:
            self.Vradar = Variable(None)
        else:
            self.Vradar = Vradar

        if Vgrid is None:
            self.Vgrid = Variable(None)
        else:
            self.Vgrid = Vgrid
        self.sharedVariables = {"Vradar": self.NewRadar,
                                "Vgrid": None}
        self.connectAllVariables()

        self.NewRadar(None, True)
        self.show()
Example #14
0
def velocity_coherency(
        radar, gatefilter=None, text_bins=40, text_limits=(0, 20),
        nyquist_vel=None, texture_window=(3, 3), texture_sample=5,
        max_texture=None, min_sweep=None, rays_wrap_around=False,
        remove_small_features=False, size_bins=100, size_limits=(0, 400),
        fill_value=None, vdop_field=None, text_field=None, coherent_field=None,
        debug=False, verbose=False):
    """

    Parameters
    ----------
    radar : Radar
        Py-ART Radar containing Doppler velocity data.
    gatefilter : GateFilter, optional
        Py-ART GateFilter instance. If None, all radar gates will initially be
        assumed valid.

    Returns
    -------
    gf : GateFilter
        Py-ART GateFilter.
    """

    # Parse fill value
    if fill_value is None:
        fill_value = get_fillvalue()

    # Parse field names
    if vdop_field is None:
        vdop_field = get_field_name('velocity')
    if text_field is None:
        text_field = '{}_texture'.format(vdop_field)
    if coherent_field is None:
        coherent_field = '{}_coherency_mask'.format(vdop_field)

    if verbose:
        print 'Computing Doppler velocity coherency mask'

    # Parse Nyquist velocity
    if nyquist_vel is None:
        nyquist = radar.get_nyquist_vel(0, check_uniform=True)

    if debug:
        print 'Radar Nyquist velocity: {:.3f} m/s'.format(nyquist)

    # Compute Doppler velocity texture field
    ray_window, gate_window = texture_window
    textures._compute_field(
        radar, vdop_field, ray_window=ray_window, gate_window=gate_window,
        min_sample=texture_sample, min_sqi=None, min_sweep=min_sweep,
        max_sweep=None, fill_value=fill_value, sqi_field=None,
        text_field=text_field)

    # Automatically determine the maximum Doppler velocity texture value which
    # brackets the coherent part of the Doppler velocity texture distribution
    if max_texture is None:

        # Bin and count Doppler velocity texture field
        counts, bin_edges = np.histogram(
            radar.fields[text_field]['data'].compressed(), bins=text_bins,
            range=text_limits, normed=False, weights=None, density=False)

        # Compute bin centers and bin width
        bin_centers = bin_edges[:-1] + np.diff(bin_edges) / 2.0
        bin_width = np.diff(bin_edges).mean()

        if debug:
            print 'Bin width: {:.3f} m/s'.format(bin_width)

        # Determine the location of extrema in the Doppler velocity texture
        # distribution
        kmin = argrelextrema(counts, np.less, order=1, mode='clip')[0]
        kmax = argrelextrema(counts, np.greater, order=1, mode='clip')[0]

        if debug:
            print 'Minima located at: {} m/s'.format(bin_centers[kmin])
            print 'Maxima located at: {} m/s'.format(bin_centers[kmax])

        # Compute the theoretical noise peak location from Guassian noise
        # statistics
        noise_peak_theory = 2.0 * nyquist / np.sqrt(12.0)

        if debug:
            print 'Theoretical noise peak: {:.3f} m/s'.format(
                noise_peak_theory)

        # Find the closest Doppler velocity texture distribution peak to the
        # computed theoretical location. Here we assume that the Doppler
        # velocity texture distribution has at least one primary mode which
        # corresponds to the incoherent (noisy) part of the Doppler velocity
        # texture distribution. Depending on the radar volume and the bin width
        # used to define the distribution, the distribution may be bimodal,
        # with the additional peak corresponding to the coherent part of the
        # Doppler velocity texture distribution
        idx = np.abs(bin_centers[kmax] - noise_peak_theory).argmin()
        noise_peak = bin_centers[kmax][idx]

        if debug:
            print 'Computed noise peak: {:.3f} m/s'.format(noise_peak)

        # Determine primary and secondary peak locations for debugging
        # purposes
        if kmax.size > 1:
            counts_max = np.sort(counts[kmax], kind='mergesort')[::-1]
            prm_peak = bin_centers[np.abs(counts - counts_max[0]).argmin()]
            sec_peak = bin_centers[np.abs(counts - counts_max[1]).argmin()]

            if debug:
                print 'Primary peak: {:.3f} m/s'.format(prm_peak)
                print 'Secondary peak: {:.3f} m/s'.format(sec_peak)

        # Determine the left edge of the noise mode
        # Where this mode becomes a minimum defines the separation between
        # coherent and incoherent Doppler velocity texture values
        # TODO: if the chosen bin width is very small than multiple extrema can
        # exist such that the first minimum to the left of the noise peak is
        # not the appropriate minimum and the left edge detection breaks down
        is_left_side = bin_centers[kmin] < noise_peak
        max_texture = bin_centers[kmin][is_left_side].min() + bin_width / 2.0

        if debug:
            _range = [0.0, round(max_texture, 3)]
            print 'Doppler velocity coherent mode: {} m/s'.format(_range)

    # Create the Doppler velocity texture coherency mask
    is_coherent = np.logical_and(
            radar.fields[text_field]['data'] >= 0.0,
            radar.fields[text_field]['data'] <= max_texture)
    is_coherent = np.ma.filled(is_coherent, False)

    # Conditional sampling checks
    for sweep, sweep_slice in enumerate(radar.iter_slice()):
        if sweep < min_sweep:
            is_coherent[sweep_slice] = False

    coherent_dict = {
        'data': is_coherent.astype(np.int8),
        'long_name': 'Doppler velocity coherency mask',
        'standard_name': coherent_field,
        'valid_min': 0,
        'valid_max': 1,
        '_FillValue': None,
        'units': 'unitless',
        'comment': '0 = incoherent velocity, 1 = coherent velocity',
    }
    radar.add_field(coherent_field, coherent_dict, replace_existing=True)

    # Remove insignificant features from Doppler velocity coherency mask
    if remove_small_features:
        basic_fixes._binary_significant_features(
            radar, coherent_field, size_bins=size_bins,
            size_limits=size_limits, structure=structure, debug=debug,
            verbose=verbose)

    # Parse gate filter
    if gatefilter is None:
        gatefilter = GateFilter(radar, exclude_based=False)

    # Update gate filter
    gatefilter.include_equal(coherent_field, 1, op='and')

    return gatefilter
Example #15
0
def _make_real_psidp_radar():
    """
    Create single-ray radar with linear differential phase profile with
    specified slope.
    ---
    Returns
    -------
    radar : Radar
            PyART radar instance with differential phase profile in deg.
    """
    psidp = np.array([[-4.63150024e-01,
                     -2.33313751e+00, 1.80617523e+00, 7.17742920e-01,
                     1.84783936e-01, 0.00000000e+00,  -2.70583344e+00,
                     3.65928650e-01,  -2.36213684e-01,  -3.37257385e-01,
                     1.61666870e-01, 2.30583191e-01, 1.09773254e+00,
                     -4.30160522e-01,  -1.47248840e+00,  -4.44534302e-01,
                     -2.71575928e-01,  -1.05207062e+00,  -1.78634644e-01,
                     -1.82469940e+00,  -1.21163177e+00,  -1.45535278e+00,
                     4.38339233e-01, 9.89532471e-03, 1.21952057e+00,
                     -3.13832092e+00, 3.32151794e+00, 2.13058472e-01,
                     -3.28111267e+00,  -9.30290222e-01, 2.05941010e+00,
                     -4.32477570e+00, 2.62687683e-01, 6.97067261e-01,
                     -2.48565674e-02, 3.63685608e+00, 1.78419495e+00,
                     -3.00376892e-01,  -1.81982422e+00, 1.29885101e+00,
                     -4.99184418e+00,  -2.15151978e+00, 5.96153259e-01,
                     -2.99251556e+00,  -1.82048035e+00, 5.45096588e+00,
                     1.88364410e+00, 2.88166809e+00, 3.60325623e+00,
                     3.64759064e+00, 4.05049896e+00, 2.48751068e+00,
                     9.06303406e+00,  -2.10025024e+00, 1.00816193e+01,
                     8.22235870e+00, 1.19126892e+01, 4.86039734e+00,
                     7.14872742e+00,  -1.95607758e+00, 3.51721954e+00,
                     -4.81446075e+00,  -1.00240326e+01, 5.14200592e+00,
                     1.10801697e-01,  -2.91020203e+00, 4.49132538e+00,
                     -4.65164185e-01, 2.01962280e+00,  -3.34449768e+00,
                     3.05713654e+00,  -4.38473511e+00, 7.73635101e+00,
                     -3.00103760e+00, 1.05568695e+00, 1.11760635e+01,
                     1.01579590e+01,  -1.64299774e+00, 1.94557953e+00,
                     -6.24208832e+00,  -9.25166321e+00, 4.27034760e+00,
                     -9.00566101e-01, 7.35614777e+00,  -3.68530273e-01,
                     1.78909302e-01, 6.77828217e+00, 2.04873657e+00,
                     6.44758606e+00, 1.55094910e+00, 1.03894043e+00,
                     1.11590118e+01,  -2.74114227e+00, 1.96223450e+00,
                     5.77777100e+00, 6.69315338e+00, 3.33438873e+00,
                     -7.87300110e-01, 2.87608337e+00,  -2.63780975e+00,
                     1.25542984e+01, 7.30712128e+00, 5.04513550e+00,
                     -1.14353180e+00, 4.77389526e+00,  -5.79799652e+00,
                     1.52477951e+01, 3.69001770e+00, 6.05420685e+00,
                     3.05950928e+00, 7.58821869e+00, 8.82480621e+00,
                     3.33154297e+00, 4.47459412e+00,  -1.89208221e+00,
                     6.25183105e+00,  -5.79544067e-01, 2.11674500e+00,
                     1.01202621e+01, 9.55703735e-01, 3.83499908e+00,
                     6.01098633e+00, 6.24042511e+00, 5.05715179e+00,
                     2.48760223e+00, 1.43062592e+00, 7.70075226e+00,
                     3.58940125e+00, 4.11083221e+00, 7.64762878e-01,
                     3.70725250e+00, 6.92240143e+00, 6.44252777e+00,
                     4.86474609e+00, 4.14801788e+00, 8.17996216e+00,
                     4.74959564e+00, 7.00319672e+00, 8.05104065e+00,
                     7.03157043e+00, 6.07535553e+00, 7.52233124e+00,
                     7.08416748e+00, 8.63216400e+00, 1.00612411e+01,
                     9.45279694e+00, 6.99411774e+00, 9.08544159e+00,
                     8.94741058e+00, 5.59152222e+00, 3.96552277e+00,
                     1.16263733e+01, 5.56375885e+00, 1.09810715e+01,
                     1.06725998e+01, 1.21604843e+01, 1.13759689e+01,
                     8.19690704e+00, 1.14421616e+01, 1.00967026e+01,
                     8.75296783e+00, 7.25759888e+00, 1.22137299e+01,
                     7.74095154e+00, 9.35678864e+00, 1.04882660e+01,
                     9.43731689e+00, 1.39332428e+01, 1.42021942e+01,
                     1.08509979e+01, 4.88009644e+00, 7.14031219e+00,
                     4.84495544e+00, 1.14619980e+01, 1.23953857e+01,
                     7.70539093e+00, 1.14259720e+01, 1.13200226e+01,
                     6.78279877e+00, 1.19683990e+01, 1.05512466e+01,
                     1.37246628e+01, 1.35013733e+01, 1.16156921e+01,
                     1.09029236e+01, 1.19279709e+01, 1.20467606e+01,
                     1.01116638e+01, 7.42034149e+00, 8.82723236e+00,
                     1.33992996e+01, 1.31808701e+01, 1.18817520e+01,
                     1.32927246e+01, 9.89555359e+00, 9.84140778e+00,
                     1.12870865e+01, 8.88268280e+00, 1.03469849e+01,
                     1.22974243e+01, 1.05293198e+01, 1.35461273e+01,
                     1.28039017e+01, 1.30093231e+01, 1.10043640e+01,
                     1.41940308e+01, 1.41589813e+01, 1.04827347e+01,
                     1.33864059e+01, 1.23348083e+01, 1.24742508e+01,
                     1.20997391e+01, 9.83790588e+00, 1.25035629e+01,
                     1.33076096e+01, 1.30602951e+01, 1.27498856e+01,
                     1.33953705e+01, 1.24620361e+01, 1.35252457e+01,
                     1.28876648e+01, 1.24350815e+01, 1.15471649e+01,
                     1.17637405e+01, 1.31310349e+01, 1.21519089e+01,
                     1.29200668e+01, 1.42670364e+01, 1.36567841e+01,
                     1.34149857e+01, 1.28509674e+01, 1.30421829e+01,
                     1.21364517e+01, 1.10667572e+01, 1.34875031e+01,
                     1.29644394e+01, 1.10929565e+01, 1.25928040e+01,
                     1.20705032e+01, 1.37613983e+01, 1.24211426e+01,
                     1.43541031e+01, 1.35802002e+01, 1.35316391e+01,
                     1.51482391e+01, 1.43572388e+01, 1.25008774e+01,
                     1.29395828e+01, 1.34839554e+01, 1.56138916e+01,
                     1.27664642e+01, 1.50478363e+01, 1.47320175e+01,
                     1.60713043e+01, 1.26680298e+01, 1.43690491e+01,
                     1.67355728e+01, 1.33732071e+01, 1.49895935e+01,
                     1.45354385e+01, 1.18312225e+01, 1.37792435e+01,
                     1.63132858e+01, 1.49929428e+01, 1.35270767e+01,
                     1.62972488e+01, 1.49179840e+01, 1.16152649e+01,
                     1.37163849e+01, 1.39367752e+01, 1.43221207e+01,
                     1.31950226e+01, 1.61372986e+01, 1.45505676e+01,
                     1.70516205e+01, 1.59943848e+01, 1.62334900e+01,
                     1.43479309e+01, 1.48412476e+01, 1.56809921e+01,
                     1.69597702e+01, 1.23267288e+01, 1.73257904e+01,
                     1.74552383e+01, 1.61041946e+01, 1.59116135e+01,
                     1.67083588e+01, 1.73401337e+01, 1.30528488e+01,
                     1.82811661e+01, 1.89352417e+01, 1.67904205e+01]])
    psidp = np.ma.array(psidp)
    radar = sample_objects.make_empty_ppi_radar(len(psidp[0]), 1, 1)
    psidp_dict = {
                  'data': psidp,
                  }
    radar.add_field(get_field_name('differential_phase'), psidp_dict)
    # Define real ranges
    radar.range['data'] = 75*np.arange(0, len(psidp[0]))
    return radar
def _make_real_psidp_radar():
    """
    Create single-ray radar with linear differential phase profile with
    specified slope.
    ---
    Returns
    -------
    radar : Radar
            PyART radar instance with differential phase profile in deg.
    """
    psidp = np.array([[
        -4.63150024e-01, -2.33313751e+00, 1.80617523e+00, 7.17742920e-01,
        1.84783936e-01, 0.00000000e+00, -2.70583344e+00, 3.65928650e-01,
        -2.36213684e-01, -3.37257385e-01, 1.61666870e-01, 2.30583191e-01,
        1.09773254e+00, -4.30160522e-01, -1.47248840e+00, -4.44534302e-01,
        -2.71575928e-01, -1.05207062e+00, -1.78634644e-01, -1.82469940e+00,
        -1.21163177e+00, -1.45535278e+00, 4.38339233e-01, 9.89532471e-03,
        1.21952057e+00, -3.13832092e+00, 3.32151794e+00, 2.13058472e-01,
        -3.28111267e+00, -9.30290222e-01, 2.05941010e+00, -4.32477570e+00,
        2.62687683e-01, 6.97067261e-01, -2.48565674e-02, 3.63685608e+00,
        1.78419495e+00, -3.00376892e-01, -1.81982422e+00, 1.29885101e+00,
        -4.99184418e+00, -2.15151978e+00, 5.96153259e-01, -2.99251556e+00,
        -1.82048035e+00, 5.45096588e+00, 1.88364410e+00, 2.88166809e+00,
        3.60325623e+00, 3.64759064e+00, 4.05049896e+00, 2.48751068e+00,
        9.06303406e+00, -2.10025024e+00, 1.00816193e+01, 8.22235870e+00,
        1.19126892e+01, 4.86039734e+00, 7.14872742e+00, -1.95607758e+00,
        3.51721954e+00, -4.81446075e+00, -1.00240326e+01, 5.14200592e+00,
        1.10801697e-01, -2.91020203e+00, 4.49132538e+00, -4.65164185e-01,
        2.01962280e+00, -3.34449768e+00, 3.05713654e+00, -4.38473511e+00,
        7.73635101e+00, -3.00103760e+00, 1.05568695e+00, 1.11760635e+01,
        1.01579590e+01, -1.64299774e+00, 1.94557953e+00, -6.24208832e+00,
        -9.25166321e+00, 4.27034760e+00, -9.00566101e-01, 7.35614777e+00,
        -3.68530273e-01, 1.78909302e-01, 6.77828217e+00, 2.04873657e+00,
        6.44758606e+00, 1.55094910e+00, 1.03894043e+00, 1.11590118e+01,
        -2.74114227e+00, 1.96223450e+00, 5.77777100e+00, 6.69315338e+00,
        3.33438873e+00, -7.87300110e-01, 2.87608337e+00, -2.63780975e+00,
        1.25542984e+01, 7.30712128e+00, 5.04513550e+00, -1.14353180e+00,
        4.77389526e+00, -5.79799652e+00, 1.52477951e+01, 3.69001770e+00,
        6.05420685e+00, 3.05950928e+00, 7.58821869e+00, 8.82480621e+00,
        3.33154297e+00, 4.47459412e+00, -1.89208221e+00, 6.25183105e+00,
        -5.79544067e-01, 2.11674500e+00, 1.01202621e+01, 9.55703735e-01,
        3.83499908e+00, 6.01098633e+00, 6.24042511e+00, 5.05715179e+00,
        2.48760223e+00, 1.43062592e+00, 7.70075226e+00, 3.58940125e+00,
        4.11083221e+00, 7.64762878e-01, 3.70725250e+00, 6.92240143e+00,
        6.44252777e+00, 4.86474609e+00, 4.14801788e+00, 8.17996216e+00,
        4.74959564e+00, 7.00319672e+00, 8.05104065e+00, 7.03157043e+00,
        6.07535553e+00, 7.52233124e+00, 7.08416748e+00, 8.63216400e+00,
        1.00612411e+01, 9.45279694e+00, 6.99411774e+00, 9.08544159e+00,
        8.94741058e+00, 5.59152222e+00, 3.96552277e+00, 1.16263733e+01,
        5.56375885e+00, 1.09810715e+01, 1.06725998e+01, 1.21604843e+01,
        1.13759689e+01, 8.19690704e+00, 1.14421616e+01, 1.00967026e+01,
        8.75296783e+00, 7.25759888e+00, 1.22137299e+01, 7.74095154e+00,
        9.35678864e+00, 1.04882660e+01, 9.43731689e+00, 1.39332428e+01,
        1.42021942e+01, 1.08509979e+01, 4.88009644e+00, 7.14031219e+00,
        4.84495544e+00, 1.14619980e+01, 1.23953857e+01, 7.70539093e+00,
        1.14259720e+01, 1.13200226e+01, 6.78279877e+00, 1.19683990e+01,
        1.05512466e+01, 1.37246628e+01, 1.35013733e+01, 1.16156921e+01,
        1.09029236e+01, 1.19279709e+01, 1.20467606e+01, 1.01116638e+01,
        7.42034149e+00, 8.82723236e+00, 1.33992996e+01, 1.31808701e+01,
        1.18817520e+01, 1.32927246e+01, 9.89555359e+00, 9.84140778e+00,
        1.12870865e+01, 8.88268280e+00, 1.03469849e+01, 1.22974243e+01,
        1.05293198e+01, 1.35461273e+01, 1.28039017e+01, 1.30093231e+01,
        1.10043640e+01, 1.41940308e+01, 1.41589813e+01, 1.04827347e+01,
        1.33864059e+01, 1.23348083e+01, 1.24742508e+01, 1.20997391e+01,
        9.83790588e+00, 1.25035629e+01, 1.33076096e+01, 1.30602951e+01,
        1.27498856e+01, 1.33953705e+01, 1.24620361e+01, 1.35252457e+01,
        1.28876648e+01, 1.24350815e+01, 1.15471649e+01, 1.17637405e+01,
        1.31310349e+01, 1.21519089e+01, 1.29200668e+01, 1.42670364e+01,
        1.36567841e+01, 1.34149857e+01, 1.28509674e+01, 1.30421829e+01,
        1.21364517e+01, 1.10667572e+01, 1.34875031e+01, 1.29644394e+01,
        1.10929565e+01, 1.25928040e+01, 1.20705032e+01, 1.37613983e+01,
        1.24211426e+01, 1.43541031e+01, 1.35802002e+01, 1.35316391e+01,
        1.51482391e+01, 1.43572388e+01, 1.25008774e+01, 1.29395828e+01,
        1.34839554e+01, 1.56138916e+01, 1.27664642e+01, 1.50478363e+01,
        1.47320175e+01, 1.60713043e+01, 1.26680298e+01, 1.43690491e+01,
        1.67355728e+01, 1.33732071e+01, 1.49895935e+01, 1.45354385e+01,
        1.18312225e+01, 1.37792435e+01, 1.63132858e+01, 1.49929428e+01,
        1.35270767e+01, 1.62972488e+01, 1.49179840e+01, 1.16152649e+01,
        1.37163849e+01, 1.39367752e+01, 1.43221207e+01, 1.31950226e+01,
        1.61372986e+01, 1.45505676e+01, 1.70516205e+01, 1.59943848e+01,
        1.62334900e+01, 1.43479309e+01, 1.48412476e+01, 1.56809921e+01,
        1.69597702e+01, 1.23267288e+01, 1.73257904e+01, 1.74552383e+01,
        1.61041946e+01, 1.59116135e+01, 1.67083588e+01, 1.73401337e+01,
        1.30528488e+01, 1.82811661e+01, 1.89352417e+01, 1.67904205e+01
    ]])
    psidp = np.ma.array(psidp)
    radar = sample_objects.make_empty_ppi_radar(len(psidp[0]), 1, 1)
    psidp_dict = {
        'data': psidp,
    }
    radar.add_field(get_field_name('differential_phase'), psidp_dict)
    # Define real ranges
    radar.range['data'] = 75 * np.arange(0, len(psidp[0]))
    return radar
Example #17
0
BINS_VDOP_COHER, LIMITS_VDOP_COHER = 100, (0, 20)
BINS_SW_COHER, LIMITS_SW_COHER = 50, (0, 5)

# Define fields to exclude from radar object
EXCLUDE_FIELDS = [
    'reflectivity',
    'differential_phase',
    'differential_reflectivity',
    'cross_correlation_ratio',
    'total_power',
    'radar_echo_classification',
    'corrected_reflectivity',
    ]

# Parse field names
VDOP_FIELD = get_field_name('velocity')
SW_FIELD = get_field_name('spectrum_width')
NCP_FIELD = get_field_name('normalized_coherent_power')

# Create histogram dictionary
HIST_DICT = {
    'number of bins': BINS_HEIGHT,
    'limits': LIMITS_HEIGHT,
    'histogram counts': np.zeros(BINS_HEIGHT, dtype=np.float64),
    }


def _loop_over_dict(
        json_file, pickle_file, inpdir=None, outdir=None, verbose=False,
        debug=False):
    """
Example #18
0
BINS_ZDR, LIMITS_ZDR = 250, (-20, 30)
BINS_NCP, LIMITS_NCP = 100, (0, 1)

# Define bins and limits for coherency (texuture) fields
BINS_VDOP_COHER, LIMITS_VDOP_COHER = 100, (0, 20)
BINS_SW_COHER, LIMITS_SW_COHER = 50, (0, 5)

# Define fields to exclude from radar object
EXCLUDE_FIELDS = [
    'radar_echo_classification',
    'corrected_reflectivity',
    'differential_phase',
    ]

# Parse field names
REFL_FIELD = get_field_name('reflectivity')
VDOP_FIELD = get_field_name('velocity')
SW_FIELD = get_field_name('spectrum_width')
RHOHV_FIELD = get_field_name('cross_correlation_ratio')
ZDR_FIELD = get_field_name('differential_reflectivity')
NCP_FIELD = get_field_name('normalized_coherent_power')
PHIDP_FIELD = get_field_name('differential_phase')

# Create histogram dictionary
HIST_DICT = {
    REFL_FIELD: {'number of bins': BINS_REFL,
                 'limits': LIMITS_REFL,
                 'histogram counts': np.zeros(BINS_REFL, dtype=np.float64),
                 },
    VDOP_FIELD: {'number of bins': BINS_VDOP,
                 'limits': LIMITS_VDOP,
import matplotlib.pyplot as plt

from netCDF4 import Dataset, num2date
from matplotlib import rcParams
from matplotlib.colors import BoundaryNorm
from matplotlib.colorbar import make_axes
from matplotlib.ticker import MultipleLocator

from pyart.graph import cm
from pyart.config import get_field_name

# Define heights to plot
HEIGHTS = [0, 5, 10, 15, 20, 25, 30, 40]

# Define radar fields
REFL_FIELD = get_field_name('reflectivity')
VDOP_FIELD = get_field_name('velocity')
CORR_VDOP_FIELD = get_field_name('corrected_velocity')
CORR_VDOP_FIELD = get_field_name('corrected_velocity')
WIDTH_FIELD = get_field_name('spectrum_width')
RHOHV_FIELD = get_field_name('cross_correlation_ratio')
ZDR_FIELD = get_field_name('differential_reflectivity')
PHIDP_FIELD = get_field_name('differential_phase')
DIST_FIELD = 'nearest_neighbor_distance'
GQI_FIELD = 'grid_quality_index'

# Define colour maps
CMAP_REFL = cm.NWSRef
CMAP_VDOP = plt.get_cmap(name='jet')
CMAP_WIDTH = plt.get_cmap(name='jet')
CMAP_RHOHV = plt.get_cmap(name='jet')
Example #20
0
def grid_radar_nearest_neighbour(
        radar, domain, fields=None, gatefilter=None, leafsize=10, legacy=False,
        proc=1, dist_field=None, time_field=None, gqi_field=None,
        range_field=None, azimuth_field=None, elevation_field=None,
        debug=False, verbose=False):
    """
    Map volumetric radar data to a rectilinear grid using nearest neighbour.

    Parameters
    ----------
    radar : pyart.core.Radar
        Radar containing the fields to be mapped.
    domain : Domain
        Grid domain.
    fields : sequence of str, optional
        Radar fields to be mapped. If None, all available radar fields are
        mapped.
    gatefilter : pyart.filters.GateFilter, optional
        GateFilter used to determine the grid quality index. If None, no grid
        quality index field is returned.

    Optional parameters
    -------------------
    max_range : float, optional
        Grid points further than `max_range` from radar are excluded from
        mapping. If None, the maximum range of the radar is used.
    leafsize : int, optional
        The number of points at which the search algorithm switches over to
        brute-force. For nearest neighbour schemes this parameter will not
        significantly change processing time.
    legacy : bool, optional
        True to return a legacy Py-ART Grid. Note that the legacy Grid is
        planned for removal altogether in future Py-ART releases.
    proc : int, optional
        Number of processes to use when querying the k-d tree.
    debug : bool, optional
        True to print debugging information, False to suppress.
    verbose : bool, optional
        True to print relevant information, False to suppress.

    Return
    ------
    grid : pyart.core.Grid
        Grid containing the mapped volumetric radar data.

    """

    # Parse field names
    if dist_field is None:
        dist_field = get_field_name('nearest_neighbor_distance')
    if time_field is None:
        time_field = get_field_name('nearest_neighbor_time')
    if gqi_field is None:
        gqi_field = get_field_name('grid_quality_index')
    if range_field is None:
        range_field = get_field_name('range')
    if azimuth_field is None:
        azimuth_field = get_field_name('azimuth')
    if elevation_field is None:
        elevation_field = get_field_name('elevation')

    # Parse fields to map
    if fields is None:
        fields = radar.fields.keys()
    if isinstance(fields, str):
        fields = [fields]
    fields = [field for field in fields if field in radar.fields]

    # Calculate radar offset relative to grid origin
    domain.compute_radar_offset_from_origin(radar, debug=debug)

    # Compute Cartesian coordinates of radar gates and apply origin offset
    zg, yg, xg = transform.equivalent_earth_model(
        radar, offset=domain.radar_offset, debug=debug, verbose=verbose)

    # Create k-d tree for radar gate locations
    # Depending on the number of radar gates this can be resource intensive
    # but nonetheless should take on the order of 1 second to create
    if verbose:
        print('Creating k-d tree instance for radar gate locations')

    tree_radar = cKDTree(
        zip(zg, yg, xg), leafsize=leafsize, compact_nodes=False,
        balanced_tree=False, copy_data=False)

    if debug:
        print('tree_radar.n = {}'.format(tree_radar.n))  # n radar gates
        print('tree_radar.m = {}'.format(tree_radar.m))  # m dimensions

    # Parse grid coordinates
    za, ya, xa = domain.coordinates
    if debug:
        print('Number of x grid points: {}'.format(domain.nx))
        print('Number of y grid points: {}'.format(domain.ny))
        print('Number of z grid points: {}'.format(domain.nz))

    # Query the radar gate k-d tree for nearest radar gates
    # This step consumes a majority of the processing time
    if verbose:
        print('Querying radar k-d tree for nearest radar gates')

    dists, idx = tree_radar.query(
        zip(za, ya, xa), k=1, p=2.0, eps=0.0,
        distance_upper_bound=np.inf, n_jobs=proc)

    if debug:
        print('Distance array shape: {}'.format(dists.shape))
        print('Minimum gate-grid distance: {:.2f} m'.format(dists.min()))
        print('Maximum gate-grid distance: {:.2f} m'.format(dists.max()))
        print('Index array shape: {}'.format(idx.shape))
        print('Minimum index: {}'.format(idx.min()))
        print('Maximum index: {}'.format(idx.max()))

    # Parse maximum range
    # Compute radar pointing directions in grid
    if max_range is None:
        max_range = radar.range['data'].max()

    _range, azimuth, elevation = transform.radar_pointing_directions(
        domain, debug=debug, verbose=verbose)
    is_far = _range > max_range

    if debug:
        n = is_far.sum()
        print('Number of grid points too far from radar: {}'.format(n))

    map_fields = {}
    for field in fields:
        if verbose:
            print('Mapping radar field: {}'.format(field))

        # Parse nearest radar data
        # Mask grid points too far from radar
        fq = radar.fields[field]['data'].flatten()[idx]
        fq = np.ma.masked_where(is_far, fq, copy=False)

        # Populate mapped radar field dictionary
        map_fields[field] = get_metadata(field)
        map_fields[field]['data'] = fq.reshape(domain.shape).astype(np.float32)
        if np.ma.is_masked(fq):
            map_fields[field]['_FillValue'] = fq.fill_value

    # Add grid quality index field
    if gatefilter is not None:

        # Parse nearest gate filter data
        # Set grid quality index to zero for grid points too far from radar
        gqi = gatefilter.gate_included.flatten()[idx]
        gqi[is_far] = 0.0

        # Populate mapped grid quality index dictionary
        map_fields[gqi_field] = get_metadata(gqi_field)
        map_fields[gqi_field]['data'] = gqi.reshape(
            domain.shape).astype(np.float32)

    # Add nearest neighbour distance field
    map_fields[dist_field] = get_metadata(dist_field)
    map_fields[dist_field]['data'] = dists.reshape(
        domain.shape).astype(np.float32)

    # Add nearest neighbor time field
    time = radar.time['data'][:, np.newaxis].repeat(
        radar.ngates, axis=1).flatten()[idx]
    map_fields[time_field] = get_metadata(time_field)
    map_fields[time_field]['data'] = time.reshape(
        domain.shape).astype(np.float32)
    map_fields[time_field]['units'] = radar.time['units']

    # Add radar range field
    map_fields[range_field] = get_metadata(range_field)
    map_fields[range_field]['data'] = _range.reshape(
        domain.shape).astype(np.float32)

    # Add radar azimuth pointing direction field
    map_fields[azimuth_field] = get_metadata(azimuth_field)
    map_fields[azimuth_field]['data'] = azimuth.reshape(
        domain.shape).astype(np.float32)

    # Add radar elevation pointing direction field
    map_fields[elevation_field] = get_metadata(elevation_field)
    map_fields[elevation_field]['data'] = elevation.reshape(
        domain.shape).astype(np.float32)

    # Populate grid metadata
    metadata = common._populate_metadata(radar, weight=None)

    if legacy:
        axes = common._populate_legacy_axes(radar, domain)
        grid = Grid.from_legacy_parameters(map_fields, axes, metadata)
    else:
        grid = Grid(map_fields, axes, metadata)  # this is incorrect

    return grid
Example #21
0
def height_histogram_from_radar(radar,
                                hist_dict,
                                gatefilter=None,
                                min_ncp=None,
                                min_sweep=None,
                                max_sweep=None,
                                min_range=None,
                                max_range=None,
                                fill_value=None,
                                ncp_field=None,
                                verbose=False,
                                debug=False):
    """
    """

    # Parse fill value
    if fill_value is None:
        fill_value = get_fillvalue()

    # Parse field names
    if ncp_field is None:
        ncp_field = get_field_name('normalized_coherent_power')

    # TODO: check input histogram dictionary for proper keys

    # Compute locations of radar gates and convert to kilometers
    x, y, heights = common.standard_refraction(radar, use_km=True)

    if debug:
        print '(Min, Max) radar gate height = ({:.3f}, {:.3f}) km'.format(
            heights.min(), heights.max())

    # Mask sweeps outside specified range
    if min_sweep is not None:
        i = radar.sweep_start_ray_index['data'][min_sweep]
        heights[:i + 1, :] = np.ma.masked
    if max_sweep is not None:
        i = radar.sweep_end_ray_index['data'][max_sweep]
        heights[i + 1:, :] = np.ma.masked

    # Mask radar range gates outside specified range
    if min_range is not None:
        i = np.abs(radar.range['data'] / 1000.0 - min_range).argmin()
        heights[:, :i + 1] = np.ma.masked
    if max_range is not None:
        i = np.abs(radar.range['data'] / 1000.0 - max_range).argmin()
        heights[:, i + 1:] = np.ma.masked

    # Mask incoherent echoes
    if min_ncp is not None:
        ncp = radar.fields[ncp_field]['data']
        heights = np.ma.masked_where(ncp < min_ncp, heights)

    # Mask excluded gates
    if gatefilter is not None:
        heights = np.ma.masked_where(gatefilter.gate_excluded, heights)

    # Parse histogram parameters
    bins = hist_dict['number of bins']
    limits = hist_dict['limits']

    # Compute histogram counts
    counts, bin_edges = np.histogram(heights.compressed(),
                                     bins=bins,
                                     range=limits,
                                     normed=False,
                                     weights=None,
                                     density=False)
    hist_dict['histogram counts'] += counts

    # Parse bin edges and bin centers
    bin_centers = bin_edges[:-1] + np.diff(bin_edges) / 2.0
    hist_dict['bin edges'] = bin_edges
    hist_dict['bin centers'] = bin_centers

    return
Example #22
0
from matplotlib.ticker import MultipleLocator

from pyart.config import get_field_name

### GLOBAL VARIABLES ###

# Input directory to moments data
INPDIR = '/aos/home/kirk/projects/clutter-classification/clutter/calibration/'

# Pickled moments data
PRECIP = 'sgpxsaprppiI4.precip.moments.pkl'
GROUND = 'sgpxsaprppiI4.ground.moments.pkl'
INSECTS = 'sgpxsaprppiI4.insects.moments.pkl'

# Parse field names
REFL_FIELD = get_field_name('reflectivity')
VDOP_FIELD = get_field_name('velocity')
SW_FIELD = get_field_name('spectrum_width')
RHOHV_FIELD = get_field_name('cross_correlation_ratio')
ZDR_FIELD = get_field_name('differential_reflectivity')
NCP_FIELD = get_field_name('normalized_coherent_power')
PHIDP_FIELD = get_field_name('differential_phase')

### Set figure parameters ###
rcParams['axes.linewidth'] = 1.5
rcParams['xtick.major.size'] = 4
rcParams['xtick.major.width'] = 1
rcParams['xtick.minor.size'] = 2
rcParams['xtick.minor.width'] = 1
rcParams['ytick.major.size'] = 4
rcParams['ytick.major.width'] = 1
Example #23
0
def histogram_from_json(
        filename, field, inpdir=None, bins=10, limits=None, min_ncp=0.5,
        vcp_sweeps=None, vcp_rays=None, min_sweep=None, max_sweep=None,
        exclude_fields=None, fill_value=None, ncp_field=None, verbose=False):
    """
    """

    # Parse fill value
    if fill_value is None:
        fill_value = get_fillvalue()

    # Parse field names
    if ncp_field is None:
        ncp_field = get_field_name('normalized_coherent_power')

    # Parse files from JSON file
    with open(filename, 'r') as fid:
        files = json.load(fid)

    # Append input directory if given
    if inpdir is not None:
        files = [os.path.join(inpdir, f) for f in files]

    if verbose:
        print 'Total number of radar files to process = %i' % len(files)

    # Loop over all files
    histogram = np.zeros(bins, dtype=np.float64)
    for f in files:

        # Read radar data
        radar = read(f, exclude_fields=exclude_fields)

        # Check radar VCP
        if vcp_sweeps is not None and radar.nsweeps != vcp_sweeps:
            continue
        if vcp_rays is not None and radar.nrays != vcp_rays:
            continue

        if verbose:
            print 'Processing file %s' % os.path.basename(f)

        # Parse radar fields
        data = radar.fields[field]['data']

        # Mask sweeps outside specified range
        if min_sweep is not None:
            i = radar.sweep_start_ray_index['data'][min_sweep]
            data[:i+1,:] = np.ma.masked
        if max_sweep is not None:
            i = radar.sweep_end_ray_index['data'][max_sweep]
            data[i+1:,:] = np.ma.masked

        # Mask incoherent echoes
        if min_ncp is not None:
            ncp = radar.fields[ncp_field]['data']
            data = np.ma.masked_where(ncp < min_ncp, data)

        # Bin data and compute frequencies
        hist, bin_edges = np.histogram(
            data.compressed(), bins=bins, range=limits, normed=False,
            weights=None, density=False)
        histogram += hist

    # Compute bin centers
    bin_centers = bin_edges[:-1] + np.diff(bin_edges) / 2.0

    # Compute normalized histogram and probability density
    histogram_norm = histogram / histogram.max()
    pdf = histogram_norm / np.sum(histogram_norm * np.diff(bin_edges))

    return {
        'field': field,
        'histogram counts': histogram,
        'normalized histogram': histogram_norm,
        'probability density': pdf,
        'number of bins': bins,
        'limits': limits,
        'bin edges': bin_edges,
        'bin centers': bin_centers,
        'radar files': [os.path.basename(f) for f in files],
        'min sweep': min_sweep,
        'max sweep': max_sweep,
        'min normalized coherent power': min_ncp,
        'sweeps in VCP': vcp_sweeps,
        'rays in VCP': vcp_rays,
        }
Example #24
0
def histograms_from_radar(
        radar, hist_dict, gatefilter=None, min_ncp=None, min_sweep=None,
        max_sweep=None, min_range=None, max_range=None, fill_value=None,
        ncp_field=None, verbose=False):
    """
    """

    # Parse fill value
    if fill_value is None:
        fill_value = get_fillvalue()

    # Parse field names
    if ncp_field is None:
        ncp_field = get_field_name('normalized_coherent_power')

    # TODO: check input histogram dictionary for proper keys

    # Loop over all fields and compute histogram counts
    for field in hist_dict:

        # Parse radar fields
        data = radar.fields[field]['data']

        # Mask sweeps outside specified range
        if min_sweep is not None:
            i = radar.sweep_start_ray_index['data'][min_sweep]
            data[:i+1,:] = np.ma.masked
        if max_sweep is not None:
            i = radar.sweep_end_ray_index['data'][max_sweep]
            data[i+1:,:] = np.ma.masked

        # Mask radar range gates outside specified range
        if min_range is not None:
            i = np.abs(radar.range['data'] / 1000.0 - min_range).argmin()
            data[:,:i+1] = np.ma.masked
        if max_range is not None:
            i = np.abs(radar.range['data'] / 1000.0 - max_range).argmin()
            data[:,i+1:] = np.ma.masked

        # Mask incoherent echoes
        if min_ncp is not None:
            ncp = radar.fields[ncp_field]['data']
            data = np.ma.masked_where(ncp < min_ncp, data)

        # Mask excluded gates
        if gatefilter is not None:
            data = np.ma.masked_where(
                gatefilter.gate_excluded, data)

        # Parse histogram parameters
        bins = hist_dict[field]['number of bins']
        limits = hist_dict[field]['limits']

        # Bin data and compute frequencies
        counts, bin_edges = np.histogram(
            data.compressed(), bins=bins, range=limits, normed=False,
            weights=None, density=False)
        hist_dict[field]['histogram counts'] += counts

        # Parse bin edges and bin centers
        bin_centers = bin_edges[:-1] + np.diff(bin_edges) / 2.0
        hist_dict[field]['bin edges'] = bin_edges
        hist_dict[field]['bin centers'] = bin_centers

    return
Example #25
0
def histograms_from_radar(radar,
                          hist_dict,
                          gatefilter=None,
                          texture_window=(3, 3),
                          texture_sample=5,
                          min_ncp=None,
                          min_sweep=None,
                          max_sweep=None,
                          min_range=None,
                          max_range=None,
                          rays_wrap_around=False,
                          fill_value=None,
                          ncp_field=None,
                          verbose=False):
    """
    """

    # Parse fill value
    if fill_value is None:
        fill_value = get_fillvalue()

    # Parse field names
    if ncp_field is None:
        ncp_field = get_field_name('normalized_coherent_power')

    # TODO: check input histogram dictionary for proper keys

    # Parse texture window parameters
    ray_window, gate_window = texture_window

    # Loop over all fields and compute histogram counts
    for field in hist_dict:

        # Compute texture fields
        _compute_field(radar,
                       field,
                       gatefilter=gatefilter,
                       ray_window=ray_window,
                       gate_window=gate_window,
                       min_sample=texture_sample,
                       min_ncp=min_ncp,
                       min_sweep=min_sweep,
                       max_sweep=max_sweep,
                       min_range=min_range,
                       max_range=max_range,
                       rays_wrap_around=rays_wrap_around,
                       fill_value=fill_value,
                       ncp_field=ncp_field)

        # Parse histogram parameters
        bins = hist_dict[field]['number of bins']
        limits = hist_dict[field]['limits']

        # Parse data and compute histogram
        data = radar.fields['{}_texture'.format(field)]['data']
        counts, bin_edges = np.histogram(data.compressed(),
                                         bins=bins,
                                         range=limits,
                                         normed=False,
                                         weights=None,
                                         density=False)
        hist_dict[field]['histogram counts'] += counts

        # Compute bin centers and add to dictionary
        bin_centers = bin_edges[:-1] + np.diff(bin_edges) / 2.0
        hist_dict[field]['bin edges'] = bin_edges
        hist_dict[field]['bin centers'] = bin_centers

    return
    def __init__(
            self,
            Vradar=None,  # Vgatefilter=None,
            name="CalculateAttenuation",
            parent=None):
        '''Initialize the class to create the interface.

        Parameters
        ----------
        [Optional]
        Vradar : :py:class:`~artview.core.core.Variable` instance
            Radar signal variable.
            A value of None initializes an empty Variable.
        name : string
            Field Radiobutton window name.
        parent : PyQt instance
            Parent instance to associate to this class.
            If None, then Qt owns, otherwise associated w/ parent PyQt instance
        '''
        super(CalculateAttenuation, self).__init__(name=name, parent=parent)
        self.central_widget = QtWidgets.QWidget()
        self.setCentralWidget(self.central_widget)
        self.layout = QtWidgets.QGridLayout(self.central_widget)

        self.despeckleButton = QtWidgets.QPushButton("CalculateAttenuation")
        self.despeckleButton.clicked.connect(self.calculate_attenuation)
        self.layout.addWidget(self.despeckleButton, 0, 0)

        parentdir = os.path.abspath(
            os.path.join(os.path.dirname(__file__), os.pardir))
        config_icon = QtGui.QIcon(
            os.sep.join([
                parentdir, 'icons', "categories-applications-system-icon.png"
            ]))
        self.configButton = QtWidgets.QPushButton(config_icon, "")
        self.layout.addWidget(self.configButton, 0, 1)
        self.configMenu = QtWidgets.QMenu(self)
        self.configButton.setMenu(self.configMenu)

        self.configMenu.addAction(
            QtWidgets.QAction("Set Parameters",
                              self,
                              triggered=self.setParameters))
        self.configMenu.addAction(
            QtWidgets.QAction("Help", self, triggered=self._displayHelp))
        self.parameters = {
            "radar": None,
            "z_offset": 0,
            "debug": False,
            "doc": 15,
            "fzl": 4000,
            "rhv_min": 0.8,
            "ncp_min": 0.5,
            "a_coef": 0.06,
            "beta": 0.8,
            "refl_field": get_field_name('reflectivity'),
            "ncp_field": get_field_name('normalized_coherent_power'),
            "rhv_field": get_field_name('cross_correlation_ratio'),
            "phidp_field": get_field_name('differential_phase'),
            "spec_at_field": get_field_name('specific_attenuation'),
            "corr_refl_field": get_field_name('corrected_reflectivity'),
        }

        self.parameters_type = [
            ("z_offset", float),
            ("debug", bool),
            ("doc", float),
            ("fzl", float),
            ("rhv_min", float),
            ("ncp_min", float),
            ("a_coef", float),
            ("beta", float),
            ("refl_field", str),
            ("ncp_field", str),
            ("rhv_field", str),
            ("phidp_field", str),
            ("spec_at_field", str),
            ("corr_refl_field", str),
        ]

        self.layout.setColumnStretch(0, 1)

        if Vradar is None:
            self.Vradar = Variable(None)
        else:
            self.Vradar = Vradar

        self.sharedVariables = {"Vradar": None}
        self.connectAllVariables()

        self.show()
Example #27
0
def _compute_field(radar,
                   field,
                   gatefilter=None,
                   ray_window=3,
                   gate_window=3,
                   min_sample=5,
                   min_ncp=None,
                   min_sweep=None,
                   max_sweep=None,
                   min_range=None,
                   max_range=None,
                   rays_wrap_around=False,
                   fill_value=None,
                   text_field=None,
                   ncp_field=None):
    """
    Compute the texture (standard deviation) within the 2-D window for the
    specified field.

    Parameters
    ----------

    Optional Parameters
    ----------------

    """

    # Parse fill value
    if fill_value is None:
        fill_value = get_fillvalue()

    # Parse field names
    if ncp_field is None:
        ncp_field = get_field_name('normalized_coherent_power')
    if text_field is None:
        text_field = '{}_texture'.format(field)

    # Parse radar data
    data = radar.fields[field]['data']

    # Mask sweeps outside of specified range
    if min_sweep is not None:
        i = radar.sweep_start_ray_index['data'][min_sweep]
        data[:i + 1, :] = np.ma.masked
    if max_sweep is not None:
        i = radar.sweep_end_ray_index['data'][max_sweep]
        data[i + 1:, :] = np.ma.masked

    # Mask radar range gates outside specified range
    if min_range is not None:
        i = np.abs(radar.range['data'] / 1000.0 - min_range).argmin()
        data[:, :i + 1] = np.ma.masked
    if max_range is not None:
        i = np.abs(radar.range['data'] / 1000.0 - max_range).argmin()
        data[:, i + 1:] = np.ma.masked

    # Mask incoherent echoes
    if min_ncp is not None and ncp_field in radar.fields:
        ncp = radar.fields[ncp_field]['data']
        data = np.ma.masked_where(ncp < min_ncp, data)

    # Mask excluded gates
    if gatefilter is not None:
        data = np.ma.masked_where(gatefilter.gate_excluded, data)

    # Prepare data for ingest into Fortran wrapper
    data = np.ma.filled(data, fill_value)
    data = np.asfortranarray(data, dtype=np.float64)

    # Parse sweep start/end indices
    # Offset indices in order to be compatible with Fortran and avoid a
    # segmentation fault
    sweep_start = radar.sweep_start_ray_index['data'] + 1
    sweep_end = radar.sweep_end_ray_index['data'] + 1

    # Compute texture field
    sample_size, texture = compute_texture.compute(data,
                                                   sweep_start,
                                                   sweep_end,
                                                   ray_window=ray_window,
                                                   gate_window=gate_window,
                                                   fill_value=fill_value)

    # Mask pixels (gates) where the sample size used to compute the texture
    # field was too small
    if min_sample is not None:
        texture = np.ma.masked_where(sample_size < min_sample,
                                     texture,
                                     copy=False)

    # Mask invalid values
    texture = np.ma.masked_equal(texture, fill_value, copy=False)
    texture = np.ma.masked_invalid(texture, copy=False)
    texture.set_fill_value(fill_value)

    # Create texture field dictionary and add it to the radar object
    texture = {
        'data':
        texture.astype(np.float32),
        'long_name':
        '{} texture'.format(radar.fields[field]['long_name']),
        'standard_name':
        text_field,
        'valid_min':
        0.0,
        '_FillValue':
        texture.fill_value,
        'units':
        radar.fields[field]['units'],
        'comment_1': ('Texture field is defined as the standard deviation '
                      'within a prescribed 2D window'),
        'comment_2':
        '{} x {} window'.format(gate_window, ray_window),
    }

    radar.add_field(text_field, texture, replace_existing=True)

    return
Example #28
0
def hildebrand_noise(radar,
                     gatefilter=None,
                     scale=1.0,
                     remove_small_features=False,
                     size_bins=75,
                     size_limits=(0, 300),
                     rays_wrap_around=False,
                     fill_value=None,
                     power_field=None,
                     noise_field=None,
                     mask_field=None,
                     verbose=False):
    """
    """

    # Parse fill value
    if fill_value is None:
        fill_value = get_fillvalue()

    # Parse field names
    if power_field is None:
        power_field = get_field_name('signal_to_noise_ratio')
    if noise_field is None:
        noise_field = 'radar_noise_floor'
    if mask_field is None:
        mask_field = 'radar_noise_floor_mask'

    # Parse radar power data
    power = radar.fields[power_field]['data']

    # Prepare data for ingest into Fortran wrapper
    power = np.ma.filled(power, fill_value)
    power = np.asfortranarray(power, dtype=np.float64)

    # Convert power units to linear and sort in descending order
    # TODO: check if units are already linear
    power = np.where(power != fill_value, 10.0**(power / 10.0), power)
    power = np.sort(power, axis=0, kind='mergesort')[::-1]

    # Fortran wrapper to Hildebrand and Sekhon (1974) algorithm
    P, Q, R2, N = sweeps.hildebrand(power, fill_value=fill_value)

    # Mask invalid data
    P = np.ma.masked_equal(P, fill_value)
    Q = np.ma.masked_equal(Q, fill_value)
    R2 = np.ma.masked_equal(R2, fill_value)
    N = np.ma.masked_equal(N, fill_value)

    # Estimate noise floor in decibels and tile to proper dimensions
    noise = 10.0 * np.ma.log10(P + scale * np.ma.sqrt(Q))
    noise = np.tile(noise, (radar.nrays, 1))
    noise.set_fill_value(fill_value)

    # Add Hildebrand noise floor field to radar
    noise_dict = {
        'data':
        noise.astype(np.float32),
        'long_name':
        'Radar noise floor estimate',
        'standard_name':
        noise_field,
        'units':
        'dB',
        '_FillValue':
        noise.fill_value,
        'comment': ('Noise floor is estimated using Hildebrand and '
                    'Sekhon (1974) algorithm'),
    }
    radar.add_field(noise_field, noise_dict, replace_existing=True)

    # Compute noise floor mask
    power = radar.fields[power_field]['data']
    noise = radar.fields[noise_field]['data']
    is_noise = np.ma.filled(power >= noise, False)

    # Create radar noise floor mask dictionary
    mask_dict = {
        'data': is_noise.astype(np.int8),
        'long_name': 'Noise floor mask',
        'standard_name': mask_field,
        'valid_min': 0,
        'valid_max': 1,
        'units': 'unitless',
        '_FillValue': None,
        'comment': '0 = below noise floor, 1 = at or above noise floor',
    }
    radar.add_field(mask_field, mask_dict, replace_existing=True)

    # Remove insignificant features from noise floor mask
    if remove_small_features:
        basic_fixes._binary_significant_features(radar,
                                                 mask_field,
                                                 size_bins=size_bins,
                                                 size_limits=size_limits,
                                                 structure=structure,
                                                 debug=debug,
                                                 verbose=verbose)

    # Parse gate filter
    if gatefilter is None:
        gatefilter = GateFilter(radar, exclude_based=False)

    # Update gate filter
    gatefilter.include_equal(mask_field, 1, op='and')

    return gatefilter
Example #29
0
def histogram_from_json(filename,
                        field,
                        inpdir=None,
                        texture_window=(3, 3),
                        min_sample=5,
                        num_bins=10,
                        limits=None,
                        min_ncp=0.5,
                        vcp_sweeps=None,
                        vcp_rays=None,
                        min_sweep=None,
                        max_sweep=None,
                        min_range=None,
                        max_range=None,
                        rays_wrap_around=False,
                        exclude_fields=None,
                        fill_value=None,
                        ncp_field=None,
                        verbose=False):
    """
    """

    # Parse fill value
    if fill_value is None:
        fill_value = get_fillvalue()

    # Parse field names
    if ncp_field is None:
        ncp_field = get_field_name('normalized_coherent_power')

    # Parse files from JSON file
    with open(filename, 'r') as fid:
        files = json.load(fid)

    # Append input directory if given
    if inpdir is not None:
        files = [os.path.join(inpdir, f) for f in files]

    if verbose:
        print 'Total number of radar files to process = %i' % len(files)

    # Parse texture window parameters
    ray_window, gate_window = texture_window

    # Loop over all files
    counts = np.zeros(num_bins, dtype=np.float64)
    for f in files:

        # Read radar data
        radar = read(f, exclude_fields=exclude_fields)

        # Check radar VCP
        if vcp_sweeps is not None and radar.nsweeps != vcp_sweeps:
            continue
        if vcp_rays is not None and radar.nrays != vcp_rays:
            continue

        if verbose:
            print 'Processing file %s' % os.path.basename(f)

        # Compute texture fields
        _compute_field(radar,
                       field,
                       ray_window=ray_window,
                       gate_window=gate_window,
                       min_sample=min_sample,
                       min_ncp=min_ncp,
                       min_sweep=min_sweep,
                       max_sweep=max_sweep,
                       min_range=min_range,
                       max_range=max_range,
                       rays_wrap_around=rays_wrap_around,
                       fill_value=fill_value,
                       ncp_field=ncp_field)

        # Parse data and compute histogram
        data = radar.fields['{}_texture'.format(field)]['data']
        hist, bin_edges = np.histogram(data.compressed(),
                                       bins=num_bins,
                                       range=limits,
                                       normed=False,
                                       weights=None,
                                       density=False)
        counts += hist

    # Compute bin centers
    bin_centers = bin_edges[:-1] + np.diff(bin_edges) / 2.0

    # Compute normalized histogram and probability density
    counts_norm = counts / counts.max()
    pdf = counts_norm / np.sum(counts_norm * np.diff(bin_edges))

    return {
        'field': '{}_texture'.format(field),
        'histogram counts': counts,
        'normalized histogram': counts_norm,
        'probability density': pdf,
        'number of bins': num_bins,
        'limits': limits,
        'bin edges': bin_edges,
        'bin centers': bin_centers,
        'radar files': [os.path.basename(f) for f in files],
        'min sweep': min_sweep,
        'max sweep': max_sweep,
        'min range': min_range,
        'max range': max_range,
        'min normalized coherent power': min_ncp,
        'sweeps in VCP': vcp_sweeps,
        'rays in VCP': vcp_rays,
        'ray window size': ray_window,
        'gate window size': gate_window,
    }
Example #30
0
def echo_boundaries(radar,
                    gatefilter=None,
                    texture_window=(3, 3),
                    texture_sample=5,
                    min_texture=None,
                    bounds_percentile=95.0,
                    remove_small_features=False,
                    size_bins=75,
                    size_limits=(0, 300),
                    rays_wrap_around=False,
                    fill_value=None,
                    sqi_field=None,
                    text_field=None,
                    bounds_field=None,
                    debug=False,
                    verbose=False):
    """
    Objectively determine the location of significant echo boundaries through
    analysis of signal quality index (SQI) texture. The a priori assumption is
    that at echo boundaries (e.g., cloud boundaries), the SQI field decreases
    substantially and therefore the SQI texture field is large near echo
    boundaries.

    Parameters
    ----------
    radar : Radar
        Radar object containing the SQI field used to derive significant echo
        boundaries.

    Returns
    -------
    gatefilter : GateFilter

    """

    # Parse fill value
    if fill_value is None:
        fill_value = get_fillvalue()

    # Parse field names
    if sqi_field is None:
        sqi_field = get_field_name('normalized_coherent_power')
    if text_field is None:
        text_field = '{}_texture'.format(sqi_field)
    if bounds_field is None:
        bounds_field = 'echo_boundaries_mask'

    if verbose:
        print 'Computing significant echo boundaries mask'

    # Compute signal quality index texture field
    ray_window, gate_window = texture_window
    texture_fields._compute_field(radar,
                                  sqi_field,
                                  ray_window=ray_window,
                                  gate_window=gate_window,
                                  min_sample=texture_sample,
                                  min_ncp=None,
                                  min_sweep=None,
                                  max_sweep=None,
                                  rays_wrap_around=rays_wrap_around,
                                  fill_value=fill_value,
                                  text_field=text_field,
                                  ncp_field=None)

    if min_texture is None:

        # The specified boundary percentile defines the minimum SQI texture
        # value for significant echo boundaries
        min_texture = np.percentile(
            radar.fields[text_field]['data'].compressed(),
            bounds_percentile,
            overwrite_input=False,
            interpolation='linear')

        if debug:
            max_texture = radar.fields[text_field]['data'].max()
            _range = [round(min_texture, 3), round(max_texture, 3)]
            print 'Echo boundary SQI texture range: {}'.format(_range)

        # Compute percentiles for debugging purposes
        percentiles = [5, 10, 25, 50, 75, 90, 95, 99, 100]
        textures = np.percentile(radar.fields[text_field]['data'].compressed(),
                                 percentiles,
                                 overwrite_input=False,
                                 interpolation='linear')

        if debug:
            for p, texture in zip(percentiles, textures):
                print '{}% SQI texture = {:.5f}'.format(p, texture)

    # Determine radar gates which meet minimum normalized coherent power
    # texture
    is_boundary = radar.fields[text_field]['data'] >= min_texture
    is_boundary = np.ma.filled(is_boundary, False)

    # Create significant echo boundaries field dictionary
    bounds_dict = {
        'data': is_boundary.astype(np.int8),
        'standard_name': bounds_field,
        'long_name': 'Significant echo boundaries mask',
        '_FillValue': None,
        'units': 'unitless',
        'comment': '0 = not an echo boundary, 1 = echo boundary',
    }
    radar.add_field(bounds_field, bounds_dict, replace_existing=True)

    # Remove insignificant features from significant echo boundaries mask
    if remove_small_features:
        basic_fixes._binary_significant_features(radar,
                                                 bounds_field,
                                                 size_bins=size_bins,
                                                 size_limits=size_limits,
                                                 structure=structure,
                                                 debug=debug,
                                                 verbose=verbose)

    # Parse gate filter
    if gatefilter is None:
        gatefilter = GateFilter(radar, exclude_based=False)

    # Update gate filter
    gatefilter.include_equal(bounds_field, 1, op='and')

    return gatefilter
Example #31
0
def hildebrand_noise(
        radar, gatefilter=None, scale=1.0, remove_small_features=False,
        size_bins=75, size_limits=(0, 300), rays_wrap_around=False,
        fill_value=None, power_field=None, noise_field=None, mask_field=None,
        verbose=False):
    """
    """

    # Parse fill value
    if fill_value is None:
        fill_value = get_fillvalue()

    # Parse field names
    if power_field is None:
        power_field = get_field_name('signal_to_noise_ratio')
    if noise_field is None:
        noise_field = 'radar_noise_floor'
    if mask_field is None:
        mask_field = 'radar_noise_floor_mask'

    # Parse radar power data
    power = radar.fields[power_field]['data']

    # Prepare data for ingest into Fortran wrapper
    power = np.ma.filled(power, fill_value)
    power = np.asfortranarray(power, dtype=np.float64)

    # Convert power units to linear and sort in descending order
    # TODO: check if units are already linear
    power = np.where(power != fill_value, 10.0**(power / 10.0), power)
    power = np.sort(power, axis=0, kind='mergesort')[::-1]

    # Fortran wrapper to Hildebrand and Sekhon (1974) algorithm
    P, Q, R2, N = sweeps.hildebrand(power, fill_value=fill_value)

    # Mask invalid data
    P = np.ma.masked_equal(P, fill_value)
    Q = np.ma.masked_equal(Q, fill_value)
    R2 = np.ma.masked_equal(R2, fill_value)
    N = np.ma.masked_equal(N, fill_value)

    # Estimate noise floor in decibels and tile to proper dimensions
    noise = 10.0 * np.ma.log10(P + scale * np.ma.sqrt(Q))
    noise = np.tile(noise, (radar.nrays, 1))
    noise.set_fill_value(fill_value)

    # Add Hildebrand noise floor field to radar
    noise_dict = {
        'data': noise.astype(np.float32),
        'long_name': 'Radar noise floor estimate',
        'standard_name': noise_field,
        'units': 'dB',
        '_FillValue': noise.fill_value,
        'comment': ('Noise floor is estimated using Hildebrand and '
                    'Sekhon (1974) algorithm'),
    }
    radar.add_field(noise_field, noise_dict, replace_existing=True)

    # Compute noise floor mask
    power = radar.fields[power_field]['data']
    noise = radar.fields[noise_field]['data']
    is_noise = np.ma.filled(power >= noise, False)

    # Create radar noise floor mask dictionary
    mask_dict = {
        'data': is_noise.astype(np.int8),
        'long_name': 'Noise floor mask',
        'standard_name': mask_field,
        'valid_min': 0,
        'valid_max': 1,
        'units': 'unitless',
        '_FillValue': None,
        'comment': '0 = below noise floor, 1 = at or above noise floor',
    }
    radar.add_field(mask_field, mask_dict, replace_existing=True)

    # Remove insignificant features from noise floor mask
    if remove_small_features:
        basic_fixes._binary_significant_features(
            radar, mask_field, size_bins=size_bins, size_limits=size_limits,
            structure=structure, debug=debug, verbose=verbose)

    # Parse gate filter
    if gatefilter is None:
        gatefilter = GateFilter(radar, exclude_based=False)

    # Update gate filter
    gatefilter.include_equal(mask_field, 1, op='and')

    return gatefilter
Example #32
0
def _spectrum_width_coherency(radar,
                              gatefilter=None,
                              num_bins=10,
                              limits=None,
                              texture_window=(3, 3),
                              texture_sample=5,
                              min_sigma=None,
                              max_sigma=None,
                              rays_wrap_around=False,
                              remove_salt=False,
                              salt_window=(5, 5),
                              salt_sample=10,
                              fill_value=None,
                              width_field=None,
                              width_text_field=None,
                              cohere_field=None,
                              verbose=False):
    """
    """

    # Parse fill value
    if fill_value is None:
        fill_value = get_fillvalue()

    # Parse field names
    if width_field is None:
        width_field = get_field_name('spectrum_width')
    if width_text_field is None:
        width_text_field = '{}_texture'.format(width_field)
    if cohere_field is None:
        cohere_field = '{}_coherency_mask'.format(width_field)

    # Compute spectrum width texture field
    ray_window, gate_window = texture_window
    texture_fields._compute_field(radar,
                                  width_field,
                                  ray_window=ray_window,
                                  gate_window=gate_window,
                                  min_sample=texture_sample,
                                  min_ncp=None,
                                  min_sweep=None,
                                  max_sweep=None,
                                  fill_value=fill_value,
                                  ncp_field=None)

    # Automatically bracket noise distribution
    if min_sigma is None and max_sigma is None:

        # Compute spectrum width texture frequency counts
        # Normalize frequency counts and compute bin centers and bin width
        width_sigma = radar.fields[width_text_field]['data']
        hist, edges = np.histogram(width_sigma.compressed(),
                                   bins=num_bins,
                                   range=limits,
                                   normed=False,
                                   weights=None,
                                   density=False)
        hist = hist.astype(np.float64) / hist.max()
        width = np.diff(edges).mean()
        half_width = width / 2.0
        bins = edges[:-1] + half_width

        if verbose:
            print 'Bin width = %.2f m/s' % width

        # Determine distribution extrema locations
        k_min = argrelextrema(hist, np.less, axis=0, order=1, mode='clip')[0]
        k_max = argrelextrema(hist, np.greater, axis=0, order=1,
                              mode='clip')[0]

        if verbose:
            print 'Minima located at %s m/s' % bins[k_min]
            print 'Maxima located at %s m/s' % bins[k_max]

        #  Potentially a clear air volume
        if k_min.size <= 1 or k_max.size <= 1:

            # Bracket noise distribution
            # Add (left side) or subtract (right side) the half bin width to
            # account for the bin width
            max_sigma = bins.max() + half_width

            # Account for the no coherent signal case
            if k_min.size == 0:
                min_sigma = bins.min() - half_width
            else:
                min_sigma = bins[k_min][0] + half_width

            if verbose:
                print 'Computed min_sigma = %.2f m/s' % min_sigma
                print 'Computed max_sigma = %.2f m/s' % max_sigma
                print 'Radar volume is likely a clear air volume'

        # Typical volume containing sufficient scatterers (e.g., hydrometeors,
        # insects, etc.)
        else:

            # Compute primary and secondary peak locations
            hist_max = np.sort(hist[k_max], kind='mergesort')[::-1]
            prm_peak = bins[np.abs(hist - hist_max[0]).argmin()]
            sec_peak = bins[np.abs(hist - hist_max[1]).argmin()]

            if verbose:
                print 'Primary peak located at %.2f m/s' % prm_peak
                print 'Secondary peak located at %.2f m/s' % sec_peak

            # If the primary (secondary) peak velocity texture is greater than
            # the secondary (primary) peak velocity texture, than the primary
            # (secondary) peak defines the noise distribution
            noise_peak = np.max([prm_peak, sec_peak])

            if verbose:
                print 'Noise peak located at %.2f m/s' % noise_peak

            # Determine left/right sides of noise distribution
            left_side = bins[k_min] < noise_peak
            right_side = bins[k_min] > noise_peak

            # Bracket noise distribution
            # Add (left side) or subtract (right side) the half bin width to
            # account for the bin width
            min_sigma = bins[k_min][left_side].max() + half_width
            max_sigma = bins.max() + half_width

            if verbose:
                print 'Computed min_sigma = %.2f m/s' % min_sigma
                print 'Computed max_sigma = %.2f m/s' % max_sigma

    # Create the spectrum width texture coherency mask
    mask = np.logical_or(radar.fields[width_text_field]['data'] <= min_sigma,
                         radar.fields[width_text_field]['data'] >= max_sigma)
    mask = np.ma.filled(mask, False)

    mask_dict = {
        'data':
        mask.astype(np.int8),
        'long_name':
        'Spectrum width coherency mask',
        'standard_name':
        cohere_field,
        'valid_min':
        0,
        'valid_max':
        1,
        '_FillValue':
        None,
        'units':
        'unitless',
        'comment': ('0 = incoherent spectrum width, '
                    '1 = coherent spectrum width'),
    }
    radar.add_field(cohere_field, mask_dict, replace_existing=True)

    # Remove salt and pepper noise from mask
    if remove_salt:
        basic_fixes.remove_salt(radar,
                                fields=[cohere_field],
                                salt_window=salt_window,
                                salt_sample=salt_sample,
                                rays_wrap_around=rays_wrap_around,
                                fill_value=0,
                                mask_data=False,
                                verbose=verbose)

    # Parse gate filter
    if gatefilter is None:
        gatefilter = GateFilter(radar, exclude_based=False)

    # Update gate filter
    gatefilter.include_equal(cohere_field, 1, op='and')

    return gatefilter
Example #33
0
def significant_detection(
        radar, gatefilter=None, remove_small_features=True, size_bins=75,
        size_limits=(0, 300), fill_holes=False, dilate=False, structure=None,
        iterations=1, rays_wrap_around=False, min_ncp=None, ncp_field=None,
        detect_field=None, debug=False, verbose=False):
    """
    Determine the significant detection of a radar. Note that significant
    detection can still include other non-meteorological echoes that the user
    may still have to remove further down the processing chain.

    Parameters
    ----------
    radar : Radar
        Radar object used to determine the appropriate GateFilter.
    gatefilter : GateFilter, optional
        If None, all radar gates will initially be assumed valid.
    remove_small_features : bool, optional
        True to remove insignificant echo features (e.g., salt and pepper
        noise) from significant detection mask.
    size_bins : int, optional
        Number of bins used to bin echo feature sizes and thus define its
        distribution.
    size_limits : list or tuple, optional
        Limits of the echo feature size distribution. The upper limit needs to
        be large enough to include the minimum feature size.
    fill_holes : bool, optional
        Fill any holes in the significant detection mask. For most radar
        volumes this should not be used since the default structuring element
        will automatically fill any sized hole.
    dilate : bool, optional
        Use binary dilation to fill in edges of the significant detection mask.
    structure : array_like, optional
        The binary structuring element used for all morphology routines. See
        SciPy's ndimage documentation for more information.
    iterations : int, optional
        The number of iterations to repeat binary dilation. If iterations is
        less than 1, binary dilation is repeated until the result does not
        change anymore.
    rays_wrap_around : bool, optional
        Whether the rays at the beginning and end of a sweep are connected
        (e.g., PPI VCP).
    min_ncp : float, optional
        Minimum normalized coherent power (signal quality) value used to
        indicate a significant echo.
    ncp_field : str, optional
        Minimum normalized coherent power (signal quality) field name. The
        default uses the Py-ART configuation file.
    detect_field : str, optional
        Radar significant detection mask field name.
    debug : bool, optional
        True to print debugging information, False to suppress.
    verbose : bool, optional
        True to print progress information, False to suppress.

    Returns
    -------
    gatefilter : GateFilter
        Py-ART GateFilter object indicating which radar gates are valid and
        invalid.
    """

    # Parse field names
    if ncp_field is None:
        ncp_field = get_field_name('normalized_coherent_power')
    if detect_field is None:
        detect_field = 'significant_detection_mask'

    # Parse gate filter
    if gatefilter is None:
        gatefilter = GateFilter(radar, exclude_based=False)

    # Exclude gates with poor signal quality
    if min_ncp is not None and ncp_field in radar.fields:
        gatefilter.include_above(ncp_field, min_ncp, op='and', inclusive=True)

    detect_dict = {
        'data': gatefilter.gate_included.astype(np.int8),
        'long_name': 'Radar significant detection mask',
        'standard_name': 'significant_detection_mask',
        'valid_min': 0,
        'valid_max': 1,
        '_FillValue': None,
        'units': 'unitless',
        'comment': '0 = no significant detection, 1 = significant detection',
    }
    radar.add_field(detect_field, detect_dict, replace_existing=True)

    # Remove insignificant features from significant detection mask
    if remove_small_features:
        basic_fixes._binary_significant_features(
            radar, detect_field, size_bins=size_bins, size_limits=size_limits,
            structure=structure, debug=debug, verbose=verbose)

    # Fill holes in significant detection mask
    if fill_holes:
        basic_fixes._binary_fill(radar, detect_field, structure=structure)

    # Dilate significant detection mask
    if dilate:
        basic_fixes._binary_dilation(
            radar, detect_field, structure=structure, iterations=iterations,
            debug=debug, verbose=verbose)

    # Update gate filter
    gatefilter.include_equal(detect_field, 1, op='new')

    return gatefilter
    def __init__(self, Vradar=None, Vgatefilter=None,
                 name="DealiasUnwrapPhase", parent=None):
        '''Initialize the class to create the interface.

        Parameters
        ----------
        [Optional]
        Vradar : :py:class:`~artview.core.core.Variable` instance
            Radar signal variable.
            A value of None initializes an empty Variable.
        name : string
            Field Radiobutton window name.
        parent : PyQt instance
            Parent instance to associate to this class.
            If None, then Qt owns, otherwise associated w/ parent PyQt instance
        '''
        super(DealiasUnwrapPhase, self).__init__(name=name, parent=parent)
        self.central_widget = QtWidgets.QWidget()
        self.setCentralWidget(self.central_widget)
        self.layout = QtWidgets.QGridLayout(self.central_widget)

        self.despeckleButton = QtWidgets.QPushButton("DealiasUnwrapPhase")
        self.despeckleButton.clicked.connect(self.dealias_unwrap_phase)
        self.layout.addWidget(self.despeckleButton, 0, 0)

        parentdir = os.path.abspath(os.path.join(os.path.dirname(__file__),
                                                 os.pardir))
        config_icon = QtGui.QIcon(os.sep.join(
            [parentdir, 'icons', "categories-applications-system-icon.png"]))
        self.configButton = QtWidgets.QPushButton(config_icon,"")
        self.layout.addWidget(self.configButton, 0, 1)
        self.configMenu = QtWidgets.QMenu(self)
        self.configButton.setMenu(self.configMenu)

        self.configMenu.addAction(QtWidgets.QAction("Set Parameters", self,
                                                triggered=self.setParameters))
        self.configMenu.addAction(QtWidgets.QAction("Help", self,
                                                triggered=self._displayHelp))
        self.parameters = {
            "radar": None,
            "gatefilter": None,
            "unwrap_unit": "sweep",
            "interval_limits": None,
            "nyquist_velocity": "",
            "check_nyquist_uniform": False,
            "rays_wrap_around": True,
            "keep_original": False,
            "vel_field": get_field_name('velocity'),
            "corr_vel_field": get_field_name('corrected_velocity'),
            "skip_checks": False
            }

        self.parameters_type = [
            ("unwrap_unit", ('ray', 'sweep', 'volume')),
            ("nyquist_velocity", float, common.float_or_none),
            ("check_nyquist_uniform", bool),
            ("rays_wrap_around", bool),
            ("keep_original", bool),
            ("vel_field", str),
            ("corr_vel_field", str),
            ("skip_checks", bool)
            ]

        self.layout.setColumnStretch(0, 1)

        if Vradar is None:
            self.Vradar = Variable(None)
        else:
            self.Vradar = Vradar

        if Vgatefilter is None:
            self.Vgatefilter = Variable(None)
        else:
            self.Vgatefilter = Vgatefilter

        self.sharedVariables = {"Vradar": None,
                                "Vgatefilter": None}
        self.connectAllVariables()

        self.show()
Example #35
0
def echo_boundaries(
        radar, gatefilter=None, texture_window=(3, 3), texture_sample=5,
        min_texture=None, bounds_percentile=95.0, remove_small_features=False,
        size_bins=75, size_limits=(0, 300), rays_wrap_around=False,
        fill_value=None, sqi_field=None, text_field=None, bounds_field=None,
        debug=False, verbose=False):
    """
    Objectively determine the location of significant echo boundaries through
    analysis of signal quality index (SQI) texture. The a priori assumption is
    that at echo boundaries (e.g., cloud boundaries), the SQI field decreases
    substantially and therefore the SQI texture field is large near echo
    boundaries.

    Parameters
    ----------
    radar : Radar
        Radar object containing the SQI field used to derive significant echo
        boundaries.

    Returns
    -------
    gatefilter : GateFilter

    """

    # Parse fill value
    if fill_value is None:
        fill_value = get_fillvalue()

    # Parse field names
    if sqi_field is None:
        sqi_field = get_field_name('normalized_coherent_power')
    if text_field is None:
        text_field = '{}_texture'.format(sqi_field)
    if bounds_field is None:
        bounds_field = 'echo_boundaries_mask'

    if verbose:
        print 'Computing significant echo boundaries mask'

    # Compute signal quality index texture field
    ray_window, gate_window = texture_window
    texture_fields._compute_field(
        radar, sqi_field, ray_window=ray_window, gate_window=gate_window,
        min_sample=texture_sample, min_ncp=None, min_sweep=None,
        max_sweep=None, rays_wrap_around=rays_wrap_around,
        fill_value=fill_value, text_field=text_field, ncp_field=None)

    if min_texture is None:

        # The specified boundary percentile defines the minimum SQI texture
        # value for significant echo boundaries
        min_texture = np.percentile(
            radar.fields[text_field]['data'].compressed(), bounds_percentile,
            overwrite_input=False, interpolation='linear')

        if debug:
            max_texture = radar.fields[text_field]['data'].max()
            _range = [round(min_texture, 3), round(max_texture, 3)]
            print 'Echo boundary SQI texture range: {}'.format(_range)

        # Compute percentiles for debugging purposes
        percentiles = [5, 10, 25, 50, 75, 90, 95, 99, 100]
        textures = np.percentile(
            radar.fields[text_field]['data'].compressed(), percentiles,
            overwrite_input=False, interpolation='linear')

        if debug:
            for p, texture in zip(percentiles, textures):
                print '{}% SQI texture = {:.5f}'.format(p, texture)

    # Determine radar gates which meet minimum normalized coherent power
    # texture
    is_boundary = radar.fields[text_field]['data'] >= min_texture
    is_boundary = np.ma.filled(is_boundary, False)

    # Create significant echo boundaries field dictionary
    bounds_dict = {
        'data': is_boundary.astype(np.int8),
        'standard_name': bounds_field,
        'long_name': 'Significant echo boundaries mask',
        '_FillValue': None,
        'units': 'unitless',
        'comment': '0 = not an echo boundary, 1 = echo boundary',
    }
    radar.add_field(bounds_field, bounds_dict, replace_existing=True)

    # Remove insignificant features from significant echo boundaries mask
    if remove_small_features:
        basic_fixes._binary_significant_features(
            radar, bounds_field, size_bins=size_bins, size_limits=size_limits,
            structure=structure, debug=debug, verbose=verbose)

    # Parse gate filter
    if gatefilter is None:
        gatefilter = GateFilter(radar, exclude_based=False)

    # Update gate filter
    gatefilter.include_equal(bounds_field, 1, op='and')

    return gatefilter
Example #36
0
def velocity_phasor_coherency(
        radar, gatefilter=None, text_bins=40, text_limits=(0, 20),
        nyquist=None, texture_window=(3, 3), texture_sample=5,
        max_texture=None, rays_wrap_around=False, remove_small_features=False,
        size_bins=75, size_limits=(0, 300), fill_value=None, vdop_field=None,
        phasor_field=None, text_field=None, coherent_field=None, debug=False,
        verbose=False):
    """
    """

    # Parse fill value
    if fill_value is None:
        fill_value = get_fillvalue()

    # Parse field names
    if vdop_field is None:
        vdop_field = get_field_name('velocity')
    if phasor_field is None:
        phasor_field = '{}_phasor_real'.format(vdop_field)
    if text_field is None:
        text_field = '{}_texture'.format(phasor_field)
    if coherent_field is None:
        coherent_field = '{}_coherency_mask'.format(phasor_field)

    if verbose:
        print 'Computing Doppler velocity phasor coherency mask'

    # Parse Nyquist velocity
    if nyquist is None:
        nyquist = radar.get_nyquist_vel(0, check_uniform=True)

    if debug:
        print 'Radar Nyquist velocity: {:.3f} m/s'.format(nyquist)

    # Compute the real part of Doppler velocity phasor
    # Normalize real part of phasor to the Nyquist interval
    vdop = radar.fields[vdop_field]['data']
    phasor_real = nyquist * np.cos(np.radians(360.0 * vdop / nyquist))

    # Mask invalid values
    phasor_real = np.ma.masked_invalid(phasor_real)
    phasor_real.set_fill_value(fill_value)

    # Create Doppler velocity phasor field dictionary
    phasor_dict = {
        'data': phasor_real.astype(np.float32),
        'long_name': 'Real part of Doppler velocity phasor',
        'standard_name': phasor_field,
        'valid_min': -nyquist,
        'valid_max': nyquist,
        '_FillValue': phasor_real.fill_value,
        'units': 'meters_per_second',
        'comment': ('Real part of Doppler velocity phasor normalized to the '
                    'Nyquist interval'),
    }
    radar.add_field(phasor_field, phasor_dict, replace_existing=True)

    # Compute Doppler velocity phasor texture field
    ray_window, gate_window = texture_window
    texture_fields._compute_field(
        radar, phasor_field, ray_window=ray_window, gate_window=gate_window,
        min_sample=texture_sample, min_ncp=None, min_sweep=None,
        max_sweep=None, fill_value=fill_value, ncp_field=None)

    # Automatically bracket coherent part of Doppler velocity phasor texture
    # distribution
    if max_texture is None:

        # Bin Doppler velocity phasor texture data and count occurrences
        # Compute bin centers and bin width
        counts, bin_edges = np.histogram(
            radar.fields[text_field]['data'].compressed(), bins=text_bins,
            range=text_limits, normed=False, weights=None, density=False)
        bin_centers = bin_edges[:-1] + np.diff(bin_edges) / 2.0
        bin_width = np.diff(bin_edges).mean()

        if debug:
            print 'Bin width: {:.3f} m/s'.format(bin_width)

        # Determine positions of the extrema in the Doppler velocity phasor
        # texture distribution
        kmin = argrelextrema(counts, np.less, order=1, mode='clip')[0]
        kmax = argrelextrema(counts, np.greater, order=1, mode='clip')[0]

        if debug:
            print 'Minima located at: {} m/s'.format(bin_centers[kmin])
            print 'Maxima located at: {} m/s'.format(bin_centers[kmax])

        # Compute the theoretical noise peak location from Guassian noise
        # statistics
        noise_peak_theory = 2.0 * nyquist / np.sqrt(12.0)

        if debug:
            print 'Theoretical noise peak: {:.3f} m/s'.format(
                noise_peak_theory)

        # Find the closest Doppler velocity phasor texture distribution peak to
        # the computed theoretical location
        # Here we assume that the Doppler velocity phasor texture distribution
        # has at least one primary mode which correspondes to the incoherent
        # (noisy) part of the Doppler velocity phasor texture distribution
        # Depending on the radar volume and the bin width used to define the
        # distribution, the distribution may be bimodal, with the new peak
        # corresponding to the coherent part of the Doppler velocity phasor
        # texture distribution
        idx = np.abs(bin_centers[kmax] - noise_peak_theory).argmin()
        noise_peak = bin_centers[kmax][idx]

        if debug:
            print 'Computed noise peak: {:.3f} m/s'.format(noise_peak)

        # Determine primary and secondary peak locations for debugging
        # purposes
        if kmax.size > 1:
            counts_max = np.sort(counts[kmax], kind='mergesort')[::-1]
            prm_peak = bin_centers[np.abs(counts - counts_max[0]).argmin()]
            sec_peak = bin_centers[np.abs(counts - counts_max[1]).argmin()]

            if debug:
                    print 'Primary peak: {:.3f} m/s'.format(prm_peak)
                    print 'Secondary peak: {:.3f} m/s'.format(sec_peak)

        # Determine the left edge of the noise distribution
        # Where this distribution becomes a minimum will define the separation
        # between coherent and incoherent Doppler velocity phasor values
        is_left_side = bin_centers[kmin] < noise_peak
        max_texture = bin_centers[kmin][is_left_side].max() + bin_width / 2.0

        if debug:
            _range = [0.0, round(max_texture, 3)]
            print 'Doppler velocity phasor coherency mode: {} m/s'.format(
                _range)

    # Create Doppler velocity phasor coherency mask
    is_coherent = np.logical_and(
        radar.fields[text_field]['data'] >= 0.0,
        radar.fields[text_field]['data'] <= max_texture)
    is_coherent = np.ma.filled(is_coherent, False)

    # Create Doppler velocity phasor coherency mask dictionary
    coherent_dict = {
        'data': is_coherent.astype(np.int8),
        'long_name': 'Doppler velocity phasor coherency',
        'standard_name': coherent_field,
        'valid_min': 0,
        'valid_max': 1,
        '_FillValue': None,
        'units': 'unitless',
        'comment': '0 = incoherent value, 1 = coherent value',
    }
    radar.add_field(coherent_field, coherent_dict, replace_existing=True)

    # Remove insignificant features from Doppler velocity phasor coherency mask
    if remove_small_features:
        basic_fixes._binary_significant_features(
            radar, coherent_field, size_bins=size_bins,
            size_limits=size_limits, structure=structure, debug=debug,
            verbose=verbose)

    # Parse gate filter
    if gatefilter is None:
        gatefilter = GateFilter(radar, exclude_based=False)

    # Update gate filter
    gatefilter.include_equal(coherent_field, 1, op='and')

    return gatefilter
Example #37
0
def _spectrum_width_coherency(
        radar, gatefilter=None, num_bins=10, limits=None,
        texture_window=(3, 3), texture_sample=5, min_sigma=None,
        max_sigma=None, rays_wrap_around=False, remove_salt=False,
        salt_window=(5, 5), salt_sample=10, fill_value=None, width_field=None,
        width_text_field=None, cohere_field=None, verbose=False):
    """
    """

    # Parse fill value
    if fill_value is None:
        fill_value = get_fillvalue()

    # Parse field names
    if width_field is None:
        width_field = get_field_name('spectrum_width')
    if width_text_field is None:
        width_text_field = '{}_texture'.format(width_field)
    if cohere_field is None:
        cohere_field = '{}_coherency_mask'.format(width_field)

    # Compute spectrum width texture field
    ray_window, gate_window = texture_window
    texture_fields._compute_field(
        radar, width_field, ray_window=ray_window, gate_window=gate_window,
        min_sample=texture_sample, min_ncp=None, min_sweep=None,
        max_sweep=None, fill_value=fill_value, ncp_field=None)

    # Automatically bracket noise distribution
    if min_sigma is None and max_sigma is None:

        # Compute spectrum width texture frequency counts
        # Normalize frequency counts and compute bin centers and bin width
        width_sigma = radar.fields[width_text_field]['data']
        hist, edges = np.histogram(
            width_sigma.compressed(), bins=num_bins, range=limits,
            normed=False, weights=None, density=False)
        hist = hist.astype(np.float64) / hist.max()
        width = np.diff(edges).mean()
        half_width = width / 2.0
        bins = edges[:-1] + half_width

        if verbose:
            print 'Bin width = %.2f m/s' % width

        # Determine distribution extrema locations
        k_min = argrelextrema(
            hist, np.less, axis=0, order=1, mode='clip')[0]
        k_max = argrelextrema(
            hist, np.greater, axis=0, order=1, mode='clip')[0]

        if verbose:
            print 'Minima located at %s m/s' % bins[k_min]
            print 'Maxima located at %s m/s' % bins[k_max]

        #  Potentially a clear air volume
        if k_min.size <= 1 or k_max.size <= 1:

            # Bracket noise distribution
            # Add (left side) or subtract (right side) the half bin width to
            # account for the bin width
            max_sigma = bins.max() + half_width

            # Account for the no coherent signal case
            if k_min.size == 0:
                min_sigma = bins.min() - half_width
            else:
                min_sigma = bins[k_min][0] + half_width

            if verbose:
                print 'Computed min_sigma = %.2f m/s' % min_sigma
                print 'Computed max_sigma = %.2f m/s' % max_sigma
                print 'Radar volume is likely a clear air volume'

        # Typical volume containing sufficient scatterers (e.g., hydrometeors,
        # insects, etc.)
        else:

            # Compute primary and secondary peak locations
            hist_max = np.sort(hist[k_max], kind='mergesort')[::-1]
            prm_peak = bins[np.abs(hist - hist_max[0]).argmin()]
            sec_peak = bins[np.abs(hist - hist_max[1]).argmin()]

            if verbose:
                print 'Primary peak located at %.2f m/s' % prm_peak
                print 'Secondary peak located at %.2f m/s' % sec_peak

            # If the primary (secondary) peak velocity texture is greater than
            # the secondary (primary) peak velocity texture, than the primary
            # (secondary) peak defines the noise distribution
            noise_peak = np.max([prm_peak, sec_peak])

            if verbose:
                print 'Noise peak located at %.2f m/s' % noise_peak

            # Determine left/right sides of noise distribution
            left_side = bins[k_min] < noise_peak
            right_side = bins[k_min] > noise_peak

            # Bracket noise distribution
            # Add (left side) or subtract (right side) the half bin width to
            # account for the bin width
            min_sigma = bins[k_min][left_side].max() + half_width
            max_sigma = bins.max() + half_width

            if verbose:
                print 'Computed min_sigma = %.2f m/s' % min_sigma
                print 'Computed max_sigma = %.2f m/s' % max_sigma

    # Create the spectrum width texture coherency mask
    mask = np.logical_or(
        radar.fields[width_text_field]['data'] <= min_sigma,
        radar.fields[width_text_field]['data'] >= max_sigma)
    mask = np.ma.filled(mask, False)

    mask_dict = {
        'data': mask.astype(np.int8),
        'long_name': 'Spectrum width coherency mask',
        'standard_name': cohere_field,
        'valid_min': 0,
        'valid_max': 1,
        '_FillValue': None,
        'units': 'unitless',
        'comment': ('0 = incoherent spectrum width, '
                    '1 = coherent spectrum width'),
    }
    radar.add_field(cohere_field, mask_dict, replace_existing=True)

    # Remove salt and pepper noise from mask
    if remove_salt:
        basic_fixes.remove_salt(
            radar, fields=[cohere_field], salt_window=salt_window,
            salt_sample=salt_sample, rays_wrap_around=rays_wrap_around,
            fill_value=0, mask_data=False, verbose=verbose)

    # Parse gate filter
    if gatefilter is None:
        gatefilter = GateFilter(radar, exclude_based=False)

    # Update gate filter
    gatefilter.include_equal(cohere_field, 1, op='and')

    return gatefilter
    ]
INNER_DOMAIN = [
    np.arange(0.0, 10500.0, 500.0),
    np.arange(-25000.0, 100500.0, 500.0),
    np.arange(-50000.0, 50500.0, 500.0)
    ]

# Analysis domain dictionary
COORDS = {
    'PBL': PBL_DOMAIN,
    'CRM': CRM_DOMAIN,
    'INNER': INNER_DOMAIN,
    }

# Define radar field names
REFL_FIELD = get_field_name('reflectivity')
REFL_CORR_FIELD = get_field_name('corrected_reflectivity')
VDOP_FIELD = get_field_name('velocity')
VDOP_CORR_FIELD = get_field_name('corrected_velocity')
SPW_FIELD = get_field_name('spectrum_width')
RHOHV_FIELD = get_field_name('cross_correlation_ratio')
ZDR_FIELD = get_field_name('differential_reflectivity')
PHIDP_FIELD = get_field_name('differential_phase')
NCP_FIELD = get_field_name('normalized_coherent_power')
SD_FIELD = get_field_name('radar_significant_detection')

# Fields to grid
FIELDS = [
    REFL_FIELD,
    REFL_CORR_FIELD,
    VDOP_FIELD,
Example #39
0
    def __init__(self, Vradar=None,  # Vgatefilter=None,
                 name="PhaseProcLp", parent=None):
        '''Initialize the class to create the interface.

        Parameters
        ----------
        [Optional]
        Vradar : :py:class:`~artview.core.core.Variable` instance
            Radar signal variable.
            A value of None initializes an empty Variable.
        name : string
            Field Radiobutton window name.
        parent : PyQt instance
            Parent instance to associate to this class.
            If None, then Qt owns, otherwise associated w/ parent PyQt instance
        '''
        super(PhaseProcLp, self).__init__(name=name, parent=parent)
        self.central_widget = QtWidgets.QWidget()
        self.setCentralWidget(self.central_widget)
        self.layout = QtWidgets.QGridLayout(self.central_widget)

        self.despeckleButton = QtWidgets.QPushButton("PhaseProcLp")
        self.despeckleButton.clicked.connect(self.phase_proc_lp)
        self.layout.addWidget(self.despeckleButton, 0, 0)

        parentdir = os.path.abspath(os.path.join(os.path.dirname(__file__),
                                                 os.pardir))
        config_icon = QtGui.QIcon(os.sep.join([parentdir, 'icons',
                                              "categories-applications-system-icon.png"]))
        self.configButton = QtWidgets.QPushButton(config_icon,"")
        self.layout.addWidget(self.configButton, 0, 1)
        self.configMenu = QtWidgets.QMenu(self)
        self.configButton.setMenu(self.configMenu)

        self.configMenu.addAction(QtWidgets.QAction("Set Parameters", self,
                                                triggered=self.setParameters))
        self.configMenu.addAction(QtWidgets.QAction("Help", self,
                                                triggered=self._displayHelp))
        self.parameters = {
            "radar": None,
            "z_offset": 0,
            "debug": False,
            "self_const": 60000,
            "low_z": 10,
            "high_z": 53,
            "min_phidp": 0.01,
            "min_ncp": 0.5,
            "min_rhv": 0.8,
            "fzl": 4000,
            "sys_phase": 0,
            "overide_sys_phase": False,
            "nowrap": None,
            "really_verbose": False,
            "LP_solver": "cylp",
            "refl_field": get_field_name('reflectivity'),
            "ncp_field": get_field_name('normalized_coherent_power'),
            "rhv_field": get_field_name('cross_correlation_ratio'),
            "phidp_field": get_field_name('differential_phase'),
            "kdp_field": get_field_name('specific_differential_phase'),
            "unf_field": get_field_name('unfolded_differential_phase'),
            "window_len": 35,
            "proc": 1,
            "reproc_phase": "repro_phase",
            "sob_kdp": "sob_kdp",
            }

        self.parameters_type = [
            ("z_offset", float),
            ("debug", bool),
            ("self_const", float),
            ("low_z", float),
            ("high_z", float),
            ("min_phidp", float),
            ("min_ncp", float),
            ("min_rhv", float),
            ("fzl", float),
            ("sys_phase", float),
            ("overide_sys_phase", bool),
            ("nowrap", int, None),
            ("really_verbose", bool),
            ("LP_solver", ("pyglpk", "cvxopt", "cylp", "cylp_mp")),
            ("refl_field", str),
            ("ncp_field", str),
            ("rhv_field", str),
            ("phidp_field", str),
            ("kdp_field", str),
            ("unf_field", str),
            ("window_len", int),
            ("proc", int),
            ("reproc_phase", str),
            ("sob_kdp", str),
            ]

        self.layout.setColumnStretch(0, 1)

        if Vradar is None:
            self.Vradar = Variable(None)
        else:
            self.Vradar = Vradar

        self.sharedVariables = {"Vradar": None}
        self.connectAllVariables()

        self.show()
Example #40
0
    def __init__(
            self,
            Vradar=None,  # Vgatefilter=None,
            name="PhaseProcLp",
            parent=None):
        '''Initialize the class to create the interface.

        Parameters
        ----------
        [Optional]
        Vradar : :py:class:`~artview.core.core.Variable` instance
            Radar signal variable.
            A value of None initializes an empty Variable.
        name : string
            Field Radiobutton window name.
        parent : PyQt instance
            Parent instance to associate to this class.
            If None, then Qt owns, otherwise associated w/ parent PyQt instance
        '''
        super(PhaseProcLp, self).__init__(name=name, parent=parent)
        self.central_widget = QtWidgets.QWidget()
        self.setCentralWidget(self.central_widget)
        self.layout = QtWidgets.QGridLayout(self.central_widget)

        self.despeckleButton = QtWidgets.QPushButton("PhaseProcLp")
        self.despeckleButton.clicked.connect(self.phase_proc_lp)
        self.layout.addWidget(self.despeckleButton, 0, 0)

        parentdir = os.path.abspath(
            os.path.join(os.path.dirname(__file__), os.pardir))
        config_icon = QtGui.QIcon(
            os.sep.join([
                parentdir, 'icons', "categories-applications-system-icon.png"
            ]))
        self.configButton = QtWidgets.QPushButton(config_icon, "")
        self.layout.addWidget(self.configButton, 0, 1)
        self.configMenu = QtWidgets.QMenu(self)
        self.configButton.setMenu(self.configMenu)

        self.configMenu.addAction(
            QtWidgets.QAction("Set Parameters",
                              self,
                              triggered=self.setParameters))
        self.configMenu.addAction(
            QtWidgets.QAction("Help", self, triggered=self._displayHelp))
        self.parameters = {
            "radar": None,
            "z_offset": 0,
            "debug": False,
            "self_const": 60000,
            "low_z": 10,
            "high_z": 53,
            "min_phidp": 0.01,
            "min_ncp": 0.5,
            "min_rhv": 0.8,
            "fzl": 4000,
            "sys_phase": 0,
            "overide_sys_phase": False,
            "nowrap": None,
            "really_verbose": False,
            "LP_solver": "cylp",
            "refl_field": get_field_name('reflectivity'),
            "ncp_field": get_field_name('normalized_coherent_power'),
            "rhv_field": get_field_name('cross_correlation_ratio'),
            "phidp_field": get_field_name('differential_phase'),
            "kdp_field": get_field_name('specific_differential_phase'),
            "unf_field": get_field_name('unfolded_differential_phase'),
            "window_len": 35,
            "proc": 1,
            "reproc_phase": "repro_phase",
            "sob_kdp": "sob_kdp",
        }

        self.parameters_type = [
            ("z_offset", float),
            ("debug", bool),
            ("self_const", float),
            ("low_z", float),
            ("high_z", float),
            ("min_phidp", float),
            ("min_ncp", float),
            ("min_rhv", float),
            ("fzl", float),
            ("sys_phase", float),
            ("overide_sys_phase", bool),
            ("nowrap", int, None),
            ("really_verbose", bool),
            ("LP_solver", ("pyglpk", "cvxopt", "cylp", "cylp_mp")),
            ("refl_field", str),
            ("ncp_field", str),
            ("rhv_field", str),
            ("phidp_field", str),
            ("kdp_field", str),
            ("unf_field", str),
            ("window_len", int),
            ("proc", int),
            ("reproc_phase", str),
            ("sob_kdp", str),
        ]

        self.layout.setColumnStretch(0, 1)

        if Vradar is None:
            self.Vradar = Variable(None)
        else:
            self.Vradar = Vradar

        self.sharedVariables = {"Vradar": None}
        self.connectAllVariables()

        self.show()
import matplotlib.pyplot as plt

from netCDF4 import Dataset, num2date
from matplotlib import rcParams
from matplotlib.colors import BoundaryNorm
from matplotlib.colorbar import make_axes
from matplotlib.ticker import MultipleLocator

from pyart.graph import cm
from pyart.config import get_field_name

# Define heights to plot
HEIGHTS = [0, 2, 4, 12, 16, 20]

# Define radar fields
REFL_FIELD = get_field_name('reflectivity')
VDOP_FIELD = get_field_name('velocity')
VDOP_CORR_FIELD = get_field_name('corrected_velocity')
SPW_FIELD = get_field_name('spectrum_width')
GQI_FIELD = get_field_name('grid_quality_index')
DIST_FIELD = get_field_name('nearest_neighbor_distance')
TIME_FIELD = get_field_name('nearest_neighbor_time')

# Define colour maps
CMAP_REFL = cm.NWSRef
CMAP_VDOP = cm.NWSVel
CMAP_SPW = cm.NWS_SPW
CMAP_GQI = cm.Carbone17
CMAP_DIST = cm.BlueBrown10

# Normalize colour maps
Example #42
0
def grid_radar(radar, domain, weight=None, fields=None, gatefilter=None,
               toa=17000.0, max_range=None, legacy=False, fill_value=None,
               dist_field=None, weight_field=None, time_field=None,
               gqi_field=None, range_field=None, azimuth_field=None,
               elevation_field=None, debug=False, verbose=False):
    """
    Map volumetric radar data to a rectilinear grid. This routine uses a k-d
    tree space-partitioning data structure for the efficient searching of the
    k-nearest neighbours.

    Parameters
    ----------
    radar : pyart.core.Radar
        Radar containing the fields to be mapped.
    domain : Domain
        Grid domain.
    weight : Weight, optional
        Weight defining the radar data objective analysis parameters and
        available kd-tree information. If None, a one-pass isotropic
        distance-dependent Barnes weight with a constant smoothing parameter
        is used.
    fields : sequence of str, optional
        Radar fields to be mapped. If None, all available radar fields are
        mapped.
    gatefilter : pyart.filters.GateFilter, optional
        GateFilter used to determine the grid quality index. If None, no grid
        quality index field is returned.

    Optional parameters
    -------------------
    toa : float, optional
        Top of the atmosphere in meters. Radar gates above this altitude are
        ignored. Lower heights will increase processing time but may also
        produce poor results if the height is similar to the top level of the
        grid.
    max_range : float, optional
        Grid points further than `max_range` from radar are excluded from
        mapping. If None, the maximum range of the radar is used.
    legacy : bool, optional
        True to return a legacy Py-ART Grid. Note that the legacy Grid is
        planned for removal altogether in future Py-ART releases.
    proc : int, optional
        Number of processes to use when querying the k-d tree.
    debug : bool, optional
        True to print debugging information, False to suppress.
    verbose : bool, optional
        True to print relevant information, False to suppress.

    Return
    ------
    grid : pyart.core.Grid
        Grid containing the mapped volumetric radar data.

    """

    # Parse fill value
    if fill_value is None:
        fill_value = get_fillvalue()

    # Parse field names
    if dist_field is None:
        dist_field = get_field_name('nearest_neighbor_distance')
    if weight_field is None:
        weight_field = get_field_name('nearest_neighbor_weight')
    if time_field is None:
        time_field = get_field_name('nearest_neighbor_time')
    if gqi_field is None:
        gqi_field = get_field_name('grid_quality_index')
    if range_field is None:
        range_field = get_field_name('range')
    if azimuth_field is None:
        azimuth_field = get_field_name('azimuth')
    if elevation_field is None:
        elevation_field = get_field_name('elevation')

    # Parse fields to map
    if fields is None:
        fields = radar.fields.keys()
    elif isinstance(fields, str):
        fields = [fields]
    fields = [field for field in fields if field in radar.fields]

    # Parse radar data objective analysis weight
    if weight is None:
        weight = Weight(radar)

    # Parse maximum range
    if max_range is None:
        max_range = radar.range['data'].max()

    # Calculate radar offset relative to the analysis grid origin
    domain.compute_radar_offset_from_origin(radar, debug=debug)

    # Compute Cartesian coordinates of radar gates relative to specified origin
    # Add reference gate locations and current gate locations to weight object
    # which will help determine if the kd-tree needs to be requeried or not
    z_g, y_g, x_g = transform.equivalent_earth_model(
        radar, offset=domain.radar_offset, debug=debug, verbose=verbose)
    weight._add_gate_reference([z_g, y_g, x_g], replace_existing=False)
    weight._add_gate_coordinates([z_g, y_g, x_g])

    if debug:
        print 'Number of radar gates before pruning: {}'.format(z_g.size)

    # Do not consider radar gates that are above the "top of the atmosphere"
    is_below_toa = z_g <= toa

    if debug:
        N = is_below_toa.sum()
        print 'Number of radar gates below TOA: {}'.format(N)

    # Slice radar coordinates below the TOA
    z_g = z_g[is_below_toa]
    y_g = y_g[is_below_toa]
    x_g = x_g[is_below_toa]

    # Slice radar data fields below the TOA but preserve original radar data
    radar_data = {}
    for field in fields:
        data = radar.fields[field]['data'].copy().flatten()
        radar_data[field] = data[is_below_toa]

    # Parse coordinates of analysis grid
    z_a, y_a, x_a = domain.z, domain.y, domain.x
    nz, ny, nx = domain.nz, domain.ny, domain.nx

    if debug:
        print 'Number of x grid points: {}'.format(nx)
        print 'Number of y grid points: {}'.format(ny)
        print 'Number of z grid points: {}'.format(nz)

    # Create analysis domain coordinates mesh
    z_a, y_a, x_a = np.meshgrid(z_a, y_a, x_a, indexing='ij')
    z_a, y_a, x_a = z_a.flatten(), y_a.flatten(), x_a.flatten()

    if debug:
        print 'Grid 1-D array shape: {}'.format(z_a.shape)

    # Query the radar gate k-d tree for the k-nearest analysis grid points.
    # Also compute the distance-dependent weights
    # This is the step that consumes the most processing time, but it can be
    # skipped if results from a similar radar volume have already computed and
    # stored in the weight object
    if weight.requery(verbose=verbose):

        # Create k-d tree object from radar gate locations
        # Depending on the number of radar gates this can be resource intensive
        # but nonetheless should take on the order of 1 second to create
        weight.create_radar_tree(
            zip(z_g, y_g, x_g), replace_existing=True, debug=debug,
            verbose=verbose)

        _, _ = weight.query_tree(
            zip(z_a, y_a, x_a), store=True, debug=debug, verbose=verbose)

        # Compute distance-dependent weights
        _ = weight.compute_weights(weight.dists, store=True, verbose=verbose)

        # Reset reference radar gate coordinates
        weight._reset_gate_reference()

    # Missing neighbors are indicated with an index set to tree.n
    # This condition will not be met for the nearest neighbor scheme, but
    # it can be met for the Cressman and Barnes schemes if the cutoff radius
    # is not large enough
    is_bad_index = weight.inds == weight.radar_tree.n

    if debug:
        N = is_bad_index.sum()
        print 'Number of invalid indices: {}'.format(N)

    # Grid points which are further than the specified maximum range away from
    # the radar should not contribute
    z_r, y_r, x_r = domain.radar_offset
    _range = np.sqrt((z_a - z_r)**2 + (y_a - y_r)**2 + (x_a - x_r)**2)
    is_far = _range > max_range

    if debug:
        N = is_far.sum()
        print('Number of analysis points too far from radar: {}'.format(N))

    # Populate grid fields
    map_fields = {}
    for field in fields:
        if verbose:
            print('Mapping radar field: {}'.format(field))

        map_fields[field] = common.populate_field(
            radar_data[field], weight.inds, (nz, ny, nx), field,
            weights=weight.wq, mask=is_far, fill_value=None)

    # Add grid quality index field
    if gatefilter is not None:

        # Compute distance-dependent weighted average of k-nearest neighbors
        # for included gates
        sqi = gatefilter.gate_included.flatten()[is_below_toa]
        gqi = np.average(sqi[weight.inds], weights=weight.wq, axis=1)
        gqi[is_far] = 0.0
        map_fields[gqi_field] = get_metadata(gqi_field)
        map_fields[gqi_field]['data'] = gqi.reshape(
            nz, ny, nx).astype(np.float32)

    # Add nearest neighbor distance field
    map_fields[dist_field] = get_metadata(dist_field)
    map_fields[dist_field]['data'] = weight.dists[:,0].reshape(
        nz, ny, nx).astype(np.float32)

    # Add nearest neighbor weight field
    map_fields[weight_field] = get_metadata(weight_field)
    map_fields[weight_field]['data'] = weight.wq[:,0].reshape(
        nz, ny, nx).astype(np.float32)

    # Add nearest neighbor time field
    time = radar.time['data'][:,np.newaxis].repeat(
        radar.ngates, axis=1).flatten()[is_below_toa][weight.inds]
    map_fields[time_field] = get_metadata(time_field)
    map_fields[time_field]['data'] = time[:,0].reshape(
        nz, ny, nx).astype(np.float32)
    map_fields[time_field]['units'] = radar.time['units']

    # Populate grid metadata
    metadata = common._populate_metadata(radar, weight=weight)

    if legacy:
        axes = common._populate_legacy_axes(radar, domain)
        grid = Grid.from_legacy_parameters(map_fields, axes, metadata)
    else:
        grid = None

    return grid
Example #43
0
def classify(radar,
             textures=None,
             moments=None,
             heights=None,
             nonprecip_map=None,
             gatefilter=None,
             weights=1.0,
             class_prob='equal',
             min_inputs=1,
             min_ncp=None,
             zero=1.0e-10,
             ignore_inputs=None,
             use_insects=True,
             fill_value=None,
             ncp_field=None,
             cloud_field=None,
             ground_field=None,
             insect_field=None,
             verbose=False):
    """
    """

    # Parse fill value
    if fill_value is None:
        fill_value = get_fillvalue()

    # Parse field names
    if ncp_field is None:
        ncp_field = get_field_name('normalized_coherent_power')
    if cloud_field is None:
        cloud_field = 'cloud'
    if ground_field is None:
        ground_field = 'ground'
    if insect_field is None:
        insect_field = 'insect'

    # Parse ignore fields
    if ignore_inputs is None:
        ignore_inputs = []

    # Check if at least one input is available
    if textures is None and moments is None:
        raise ValueError('No inputs specified')

    # Parse classification labels
    if use_insects:
        labels = [cloud_field, ground_field, insect_field]
    else:
        labels = [cloud_field, ground_field]
        if textures is not None:
            textures.pop(insect_field, None)
        if moments is not None:
            moments.pop(insect_field, None)
        if heights is not None:
            heights.pop(insect_field, None)

    # Determine total number of inputs available for each class
    inputs = {label: 0 for label in labels}
    if textures is not None:
        for label, texture in textures.iteritems():
            fields = [field for field in texture if field not in ignore_inputs]
            inputs[label] += len(fields)
    if moments is not None:
        for label, moment in moments.iteritems():
            fields = [field for field in moment if field not in ignore_inputs]
            inputs[label] += len(fields)
    if heights is not None:
        for label in labels:
            inputs[label] += 1
    if ground_map is not None:
        inputs[ground_field] += 1

    if verbose:
        for label in labels:
            print 'Total number of inputs for {} = {}'.format(
                label, inputs[label])

    # Parse class probability P(c)
    if class_prob.upper() == 'EQUAL':
        P_c = 0.5

    # Parse input probabilities P(x1, x2, ... , xn)
    if isinstance(weights, float):
        P_xi = weights

    # Initialize total probability and number of inputs arrays
    P_tot = {
        label: np.ones(radar.fields[ncp_field]['data'].shape, dtype=np.float64)
        for label in labels
    }
    num_inputs = {
        label: np.zeros(radar.fields[ncp_field]['data'].shape, dtype=np.int32)
        for label in labels
    }

    # Process radar texture fields
    if textures is not None:
        for label, texture in textures.iteritems():
            for field, histogram in texture.iteritems():

                if field in ignore_inputs:
                    continue

                # Parse radar texture data and its distribution
                data = radar.fields[field]['data']
                pdf = histogram['probability density']
                bins = histogram['bin centers']

                # Mask incoherent gates
                if min_ncp is not None and ncp_field in radar.fields:
                    ncp = radar.fields[ncp_field]['data']
                    data = np.ma.masked_where(ncp < min_ncp, data)

                # Prepare data for ingest into Fortran wrapper
                data = np.ma.filled(data, fill_value)
                data = np.asfortranarray(data, dtype=np.float64)

                # Compute conditional probability for each radar gate
                P_cond = member.conditional_all(data,
                                                pdf,
                                                bins,
                                                zero=zero,
                                                fill_value=fill_value)

                # Determine where conditional probability is valid
                valid_prob = P_cond != fill_value
                num_inputs[label] += valid_prob

                # Bayes classifier
                P_tot[label][valid_prob] *= P_c * P_cond[valid_prob] / P_xi

    # Process radar moments
    if moments is not None:
        for label, moment in moments.iteritems():
            for field, histogram in moment.iteritems():

                if field in ignore_inputs:
                    continue

                # Parse radar moment data and its distribution
                data = radar.fields[field]['data']
                pdf = histogram['probability density']
                bins = histogram['bin centers']

                # Mask incoherent gates
                if min_ncp is not None and ncp_field in radar.fields:
                    ncp = radar.fields[ncp_field]['data']
                    data = np.ma.masked_where(ncp < min_ncp, data)

                # Prepare data for ingest into Fortran wrapper
                data = np.ma.filled(data, fill_value)
                data = np.asfortranarray(data, dtype=np.float64)

                # Compute conditional probability for each radar gate
                P_cond = member.conditional_all(data,
                                                pdf,
                                                bins,
                                                zero=zero,
                                                fill_value=fill_value)

                # Determine where conditional probability is valid
                valid_prob = P_cond != fill_value
                num_inputs[label] += valid_prob

                # Bayes classifier
                P_tot[label][valid_prob] *= P_c * P_cond[valid_prob] / P_xi

    # Process radar gate heights
    if heights is not None:
        for label in labels:

            # Parse height distribution data
            pdf = heights[label]['probability density']
            bins = heights[label]['bin centers']

            # Compute radar gate heights in kilometers
            x, y, data = common.standard_refraction(radar, use_km=True)

            # Prepare data for ingest into Fortran wrapper
            data = np.ma.filled(data, fill_value)
            data = np.asfortranarray(data, dtype=np.float64)

            # Compute conditional probability for each radar gate
            P_cond = member.conditional_all(data,
                                            pdf,
                                            bins,
                                            zero=zero,
                                            fill_value=fill_value)

            # Determine where conditional probability is valid
            valid_prob = P_cond != fill_value
            num_inputs[label] += valid_prob

            # Bayes classifier
            P_tot[label][valid_prob] *= P_c * P_cond[valid_prob] / P_xi

    # Process ground frequency map
    if ground_map is not None:
        pdf = ground_map['ground frequency map']

    # Mask gates where not enough inputs were available to properly classify
    for label, sample_size in num_inputs.iteritems():
        P_tot[label] = np.ma.masked_where(sample_size < min_inputs,
                                          P_tot[label])

    # Mask excluded gates from gate filter
    if gatefilter is not None:
        for label in P_tot:
            P_tot[label] = np.ma.masked_where(gatefilter.gate_excluded,
                                              P_tot[label])

    # Determine where each class is most probable
    echo = np.zeros(P_tot[cloud_field].shape, dtype=np.int8)
    if use_insects:
        is_ground = np.logical_and(P_tot[ground_field] > P_tot[cloud_field],
                                   P_tot[ground_field] > P_tot[insect_field])
        is_insect = np.logical_and(P_tot[insect_field] > P_tot[ground_field],
                                   P_tot[insect_field] > P_tot[cloud_field])
        is_missing = P_tot[ground_field].mask
        echo[is_ground] = 1
        echo[is_insect] = 2
        echo[is_missing] = -1

    else:
        is_ground = P_tot[ground_field] > P_tot[cloud_field]
        is_missing = P_tot[ground_field].mask
        echo[is_ground] = 1
        echo[is_missing] = -1

    # Create echo classification dictionary and add it to the radar object
    echo = {
        'data':
        echo.astype(np.int8),
        'long_name':
        'Radar echo classification',
        'standard_name':
        'radar_echo_classification',
        '_FillValue':
        None,
        'units':
        'unitless',
        'comment': ('-1 = Missing gate, 0 = Cloud or precipitation, '
                    '1 = Ground clutter, 2 = Insects')
    }
    radar.add_field('radar_echo_classification', echo, replace_existing=True)

    return
import matplotlib.pyplot as plt

from netCDF4 import Dataset, num2date
from matplotlib import rcParams
from matplotlib.colors import BoundaryNorm
from matplotlib.colorbar import make_axes
from matplotlib.ticker import MultipleLocator

from pyart.graph import cm
from pyart.config import get_field_name

# Define heights to plot
HEIGHTS = [0, 2, 4, 12, 16, 20]

# Define radar fields
REFL_FIELD = get_field_name('reflectivity')
VDOP_FIELD = get_field_name('velocity')
VDOP_CORR_FIELD = get_field_name('corrected_velocity')
SPW_FIELD = get_field_name('spectrum_width')
RHOHV_FIELD = get_field_name('cross_correlation_ratio')
ZDR_FIELD = get_field_name('differential_reflectivity')
PHIDP_FIELD = get_field_name('differential_phase')
NCP_FIELD = get_field_name('normalized_coherent_power')
DIST_FIELD = get_field_name('nearest_neighbor_distance')
TIME_FIELD = get_field_name('nearest_neighbor_time')
GQI_FIELD = get_field_name('grid_quality_index')

# Define colour maps
CMAP_REFL = cm.NWSRef
CMAP_VDOP = cm.NWSVel
CMAP_SPW = cm.NWS_SPW
Example #45
0
def map_from_json(
        filename, inpdir=None, vcp_sweeps=None, vcp_rays=None, vcp_gates=None,
        min_ncp=None, use_filter=True, texture_window=(3, 3), texture_sample=5,
        vdop_bins=100, vdop_limits=(0, 20), sw_bins=50, sw_limits=(0, 5),
        remove_salt=True, salt_window=(5, 5), salt_sample=10,
        exclude_fields=None, ncp_field=None, debug=False, verbose=False):
    """
    Compute the non-precipitating frequency (probability) map from the files
    listed in a JSON file. The listed files should define a non-precipitating
    time period where (most) echoes present must be, by definition, not
    precipitation or cloud.

    Parameters
    ----------

    Optional Parameters
    -------------------

    Returns
    -------
    """

    if ncp_field is None:
        ncp_field = get_field_name('normalized_coherent_power')

    # Parse files from JSON file
    with open(filename, 'r') as fid:
        files = json.load(fid)

    # Append input directory if provided
    if inpdir is not None:
        files = [os.path.join(inpdir, f) for f in files]

    if verbose:
        print 'Total number of radar files to process = %i' % len(files)

    # Parse non-precipitating frequency map
    if vcp_rays is not None and vcp_gates is not None:
        nonprecip = np.zeros((vcp_rays, vcp_gates), dtype=np.float64)
    else:
        nonprecip = None

    # Loop over all files
    sample_size = 0
    for i, f in enumerate(files):

        # Read radar data
        radar = read(f, exclude_fields=exclude_fields)

        # Check radar VCP parameters
        if vcp_sweeps is not None and radar.nsweeps != vcp_sweeps:
            continue
        if vcp_rays is not None and radar.nrays != vcp_rays:
            continue
        if vcp_gates is not None and radar.ngates != vcp_gates:
            continue

        if verbose:
            print 'Processing file %s' % os.path.basename(f)

        # Initialize the non-precipitation frequency map if it does not exist
        if i == 0 and nonprecip is None:
            vcp_sweeps = radar.nsweeps
            vcp_rays = radar.nrays
            vcp_gates = radar.ngates
            nonprecip = np.zeros((vcp_rays, vcp_gates), dtype=np.float64)

            if verbose:
                print 'VCP sweeps = {}'.format(vcp_sweeps)
                print 'VCP rays = {}'.format(vcp_rays)
                print 'VCP gates = {}'.format(vcp_gates)

        # Increase sample size
        sample_size += 1

        # Determine significant detection
        if use_filter:

            # Doppler velocity coherency
            gatefilter = noise.velocity_coherency(
                radar, gatefilter=None, num_bins=vdop_bins, limits=vdop_limits,
                texture_window=texture_window, texture_sample=texture_sample,
                min_sigma=None, max_sigma=None, nyquist=None,
                rays_wrap_around=False, remove_salt=remove_salt,
                salt_window=salt_window, salt_sample=salt_sample,
                fill_value=None, verbose=verbose)

            # Spectrum width coherency
            gatefilter = noise.spectrum_width_coherency(
                radar, gatefilter=gatefilter, num_bins=sw_bins,
                limits=sw_limits, texture_window=texture_window,
                texture_sample=texture_sample, min_sigma=None, max_sigma=None,
                rays_wrap_around=False, remove_salt=remove_salt,
                salt_window=salt_window, salt_sample=salt_sample,
                fill_value=None, verbose=verbose)

            # Significant detection
            gatefilter = noise.significant_detection(
                radar, gatefilter=gatefilter, remove_salt=remove_salt,
                salt_window=salt_window, salt_sample=salt_sample,
                min_ncp=min_ncp, detect_field=None, verbose=verbose)

            # Parse gate filter
            is_coherent = gatefilter.gate_included.astype(np.float64)

        elif min_ncp is not None:
            is_coherent = radar.fields[ncp_field]['data'] >= min_ncp
            is_coherent = np.ma.filled(is_coherent, False).astype(np.float64)

        else:
            raise ValueError('No way to determine significant detection')

        # Increase the non-precipitation map for all coherent gates (pixels)
        nonprecip += is_coherent

    # Compute the probability a gate (pixel) has a valid echo during
    # non-precipitating events
    nonprecip_map = nonprecip / sample_size

    # Add clutter frequency map to (last) radar object
    nonprecip = {
        'data': nonprecip_map,
        'long_name': 'Non-precipitating (clutter) frequency map',
        'standard_name': 'clutter_map',
        'valid_min': 0.0,
        'valid_max': 1.0,
        '_FillValue': None,
        'units': None,
    }
    radar.add_field('clutter_map', nonprecip, replace_existing=False)

    return {
        'non-precipitating map': nonprecip_map,
        'last radar': radar,
        'sample size': sample_size,
        'radar files': [os.path.basename(f) for f in files],
        'vcp_sweeps': vcp_sweeps,
        'vcp_rays': vcp_rays,
        'vcp_gates': vcp_gates,
        'min_ncp': min_ncp,
        'use_filter': use_filter,
        'texture_window': texture_window,
        'texture_sample': texture_sample,
        'remove_salt': remove_salt,
        'salt_window': salt_window,
        'salt_sample': salt_sample,
    }
Example #46
0
def classify(radar, textures=None, moments=None, heights=None,
             nonprecip_map=None, gatefilter=None, weights=1.0,
             class_prob='equal', min_inputs=1, min_ncp=None, zero=1.0e-10,
             ignore_inputs=None, use_insects=True, fill_value=None,
             ncp_field=None, cloud_field=None, ground_field=None,
             insect_field=None, verbose=False):
    """
    """

    # Parse fill value
    if fill_value is None:
        fill_value = get_fillvalue()

    # Parse field names
    if ncp_field is None:
        ncp_field = get_field_name('normalized_coherent_power')
    if cloud_field is None:
        cloud_field = 'cloud'
    if ground_field is None:
        ground_field = 'ground'
    if insect_field is None:
        insect_field = 'insect'

    # Parse ignore fields
    if ignore_inputs is None:
        ignore_inputs = []

    # Check if at least one input is available
    if textures is None and moments is None:
        raise ValueError('No inputs specified')

    # Parse classification labels
    if use_insects:
        labels = [cloud_field, ground_field, insect_field]
    else:
        labels = [cloud_field, ground_field]
        if textures is not None:
            textures.pop(insect_field, None)
        if moments is not None:
            moments.pop(insect_field, None)
        if heights is not None:
            heights.pop(insect_field, None)

    # Determine total number of inputs available for each class
    inputs = {label: 0 for label in labels}
    if textures is not None:
        for label, texture in textures.iteritems():
            fields = [field for field in texture if field not in ignore_inputs]
            inputs[label] += len(fields)
    if moments is not None:
        for label, moment in moments.iteritems():
            fields = [field for field in moment if field not in ignore_inputs]
            inputs[label] += len(fields)
    if heights is not None:
        for label in labels:
            inputs[label] += 1
    if ground_map is not None:
        inputs[ground_field] += 1

    if verbose:
        for label in labels:
            print 'Total number of inputs for {} = {}'.format(
                label, inputs[label])

    # Parse class probability P(c)
    if class_prob.upper() == 'EQUAL':
        P_c = 0.5

    # Parse input probabilities P(x1, x2, ... , xn)
    if isinstance(weights, float):
        P_xi = weights

    # Initialize total probability and number of inputs arrays
    P_tot = {
        label: np.ones(radar.fields[ncp_field]['data'].shape, dtype=np.float64)
        for label in labels
        }
    num_inputs = {
        label: np.zeros(radar.fields[ncp_field]['data'].shape, dtype=np.int32)
        for label in labels
        }

    # Process radar texture fields
    if textures is not None:
        for label, texture in textures.iteritems():
            for field, histogram in texture.iteritems():

                if field in ignore_inputs:
                    continue

                # Parse radar texture data and its distribution
                data = radar.fields[field]['data']
                pdf = histogram['probability density']
                bins = histogram['bin centers']

                # Mask incoherent gates
                if min_ncp is not None and ncp_field in radar.fields:
                    ncp = radar.fields[ncp_field]['data']
                    data = np.ma.masked_where(ncp < min_ncp, data)

                # Prepare data for ingest into Fortran wrapper
                data = np.ma.filled(data, fill_value)
                data = np.asfortranarray(data, dtype=np.float64)

                # Compute conditional probability for each radar gate
                P_cond = member.conditional_all(
                data, pdf, bins, zero=zero, fill_value=fill_value)

                # Determine where conditional probability is valid
                valid_prob = P_cond != fill_value
                num_inputs[label] += valid_prob

                # Bayes classifier
                P_tot[label][valid_prob] *= P_c * P_cond[valid_prob] / P_xi

    # Process radar moments
    if moments is not None:
        for label, moment in moments.iteritems():
            for field, histogram in moment.iteritems():

                if field in ignore_inputs:
                    continue

                # Parse radar moment data and its distribution
                data = radar.fields[field]['data']
                pdf = histogram['probability density']
                bins = histogram['bin centers']

                # Mask incoherent gates
                if min_ncp is not None and ncp_field in radar.fields:
                    ncp = radar.fields[ncp_field]['data']
                    data = np.ma.masked_where(ncp < min_ncp, data)

                # Prepare data for ingest into Fortran wrapper
                data = np.ma.filled(data, fill_value)
                data = np.asfortranarray(data, dtype=np.float64)

                # Compute conditional probability for each radar gate
                P_cond = member.conditional_all(
                    data, pdf, bins, zero=zero, fill_value=fill_value)

                # Determine where conditional probability is valid
                valid_prob = P_cond != fill_value
                num_inputs[label] += valid_prob

                # Bayes classifier
                P_tot[label][valid_prob] *= P_c * P_cond[valid_prob] / P_xi

    # Process radar gate heights
    if heights is not None:
        for label in labels:

            # Parse height distribution data
            pdf = heights[label]['probability density']
            bins = heights[label]['bin centers']

            # Compute radar gate heights in kilometers
            x, y, data = common.standard_refraction(radar, use_km=True)

            # Prepare data for ingest into Fortran wrapper
            data = np.ma.filled(data, fill_value)
            data = np.asfortranarray(data, dtype=np.float64)

            # Compute conditional probability for each radar gate
            P_cond = member.conditional_all(
                data, pdf, bins, zero=zero, fill_value=fill_value)

            # Determine where conditional probability is valid
            valid_prob = P_cond != fill_value
            num_inputs[label] += valid_prob

            # Bayes classifier
            P_tot[label][valid_prob] *= P_c * P_cond[valid_prob] / P_xi

    # Process ground frequency map
    if ground_map is not None:
        pdf = ground_map['ground frequency map']

    # Mask gates where not enough inputs were available to properly classify
    for label, sample_size in num_inputs.iteritems():
        P_tot[label] = np.ma.masked_where(
            sample_size < min_inputs, P_tot[label])

    # Mask excluded gates from gate filter
    if gatefilter is not None:
        for label in P_tot:
            P_tot[label] = np.ma.masked_where(
                gatefilter.gate_excluded, P_tot[label])

    # Determine where each class is most probable
    echo = np.zeros(P_tot[cloud_field].shape, dtype=np.int8)
    if use_insects:
        is_ground = np.logical_and(
            P_tot[ground_field] > P_tot[cloud_field],
            P_tot[ground_field] > P_tot[insect_field])
        is_insect = np.logical_and(
            P_tot[insect_field] > P_tot[ground_field],
            P_tot[insect_field] > P_tot[cloud_field])
        is_missing = P_tot[ground_field].mask
        echo[is_ground] = 1
        echo[is_insect] = 2
        echo[is_missing] = -1

    else:
        is_ground = P_tot[ground_field] > P_tot[cloud_field]
        is_missing = P_tot[ground_field].mask
        echo[is_ground] = 1
        echo[is_missing] = -1

    # Create echo classification dictionary and add it to the radar object
    echo = {
        'data': echo.astype(np.int8),
        'long_name': 'Radar echo classification',
        'standard_name': 'radar_echo_classification',
        '_FillValue': None,
        'units': 'unitless',
        'comment': ('-1 = Missing gate, 0 = Cloud or precipitation, '
                    '1 = Ground clutter, 2 = Insects')
    }
    radar.add_field('radar_echo_classification', echo, replace_existing=True)

    return
Example #47
0
def map_date_range(start, stop, stamp, inpdir, date_str='[0-9]{12}',
                   date_fmt='%y%m%d%H%M%S', min_ncp=None, vcp_sweeps=None,
                   vcp_rays=None, exclude_fields=None, ncp_field=None,
                   debug=False, verbose=False):
    """
    Compute the clutter frequency (probability) map within the specified date
    range. The start and stop times should define a non-precipitating time
    period where (most) echoes present must be, by definition, clutter.

    Parameters
    ----------

    Optional Parameters
    -------------------

    Returns
    -------
    """

    # Parse field names
    if refl_field is None:
        refl_field = get_field_name('reflectivity')
    if ncp_field is None:
        ncp_field = get_field_name('normalized_coherent_power')

    # Get all files with stamp in directory
    files = [os.path.join(inpdir, f) for f in sorted(os.listdir(inpdir))
             if stamp in f]

    if verbose:
        print 'Total number of radar files found = %i' % len(files)

    # Remove files outside date range
    time_str = [re.search(date_str, f).group() for f in files]
    times = [datetime.strptime(string, date_fmt) for string in time_str]
    files = [
        f for f, time in zip(files, times) if time >= start and time <= stop]

    if verbose:
        print 'Number of radar files after within date range = %i' % len(files)

    if vcp_sweeps is not None and vcp_rays is not None:
        nonprecip = np.zeros((vcp_sweeps, vcp_rays), dtype=np.float64)
    else:
        nonprecip = None

    # Loop over all files
    sample_size = 0
    for i, f in enumerate(files):

        if verbose:
            print 'Processing file %s' % os.path.basename(f)

        # Read radar data
        radar = read(f, exclude_fields=exclude_fields)

        # Check radar VCP
        if vcp_sweeps is not None and radar.nsweeps != vcp_sweeps:
            continue
        if vcp_rays is not None and radar.nrays != vcp_rays:
            continue

        # Initialize the non-precipitation map if not already done so
        if i == 0 and nonprecip is None:
            nonprecip = np.zeros((radar.nrays, radar.ngates), dtype=np.float64)

        # Increase sample size
        sample_size += 1

        # Find coherent pixels
        if min_ncp is not None:
            is_coherent = radar.fields[ncp_field]['data'] >= min_ncp
            is_coherent = np.ma.filled(is_coherent, Fales).astype(np.float64)
        else:
            is_coherent = np.zeros(nonprecip.shape, dtype=np.float64)

        # Find pixels that have a coherent signal
        nonprecip += is_coherent

    # Compute the probability a pixel (gate) has a valid echo during
    # non-precipitating events
    nonprecip_map = nonprecip / sample_size

    # Add clutter frequency map to radar object
    nonprecip = {
        'data': nonprecip_map,
        'long_name': 'Non-precipitating frequency map',
        'standard_name': 'nonprecip_map',
        'valid_min': 0.0,
        'valid_max': 1.0,
        '_FillValue': None,
        'units': None,
    }
    radar.add_field('nonprecip_map', nonprecip, replace_existing=False)

    return {
        'non-precipitating map': nonprecip_map,
        'last radar': radar,
        'sample size': sample_size,
        'radar files': [os.path.basename(f) for f in files],
        'sweeps in VCP': vcp_sweeps,
        'rays in VCP': vcp_rays,
        'min NCP': min_ncp,
    }
Example #48
0
BINS_VDOP_COHER, LIMITS_VDOP_COHER = 100, (0, 20)
BINS_SW_COHER, LIMITS_SW_COHER = 50, (0, 5)

# Define fields to exclude from radar object
EXCLUDE_FIELDS = [
    "reflectivity",
    "differential_phase",
    "differential_reflectivity",
    "cross_correlation_ratio",
    "total_power",
    "radar_echo_classification",
    "corrected_reflectivity",
]

# Parse field names
VDOP_FIELD = get_field_name("velocity")
SW_FIELD = get_field_name("spectrum_width")
NCP_FIELD = get_field_name("normalized_coherent_power")

# Create histogram dictionary
HIST_DICT = {
    "number of bins": BINS_HEIGHT,
    "limits": LIMITS_HEIGHT,
    "histogram counts": np.zeros(BINS_HEIGHT, dtype=np.float64),
}


def _loop_over_dict(json_file, pickle_file, inpdir=None, outdir=None, verbose=False, debug=False):
    """
    """
Example #49
0
def height_histogram_from_radar(
        radar, hist_dict, gatefilter=None, min_ncp=None, min_sweep=None,
        max_sweep=None, min_range=None, max_range=None, fill_value=None,
        ncp_field=None, verbose=False, debug=False):
    """
    """

    # Parse fill value
    if fill_value is None:
        fill_value = get_fillvalue()

    # Parse field names
    if ncp_field is None:
        ncp_field = get_field_name('normalized_coherent_power')

    # TODO: check input histogram dictionary for proper keys

    # Compute locations of radar gates and convert to kilometers
    x, y, heights = common.standard_refraction(radar, use_km=True)

    if debug:
        print '(Min, Max) radar gate height = ({:.3f}, {:.3f}) km'.format(
            heights.min(), heights.max())

    # Mask sweeps outside specified range
    if min_sweep is not None:
        i = radar.sweep_start_ray_index['data'][min_sweep]
        heights[:i+1,:] = np.ma.masked
    if max_sweep is not None:
        i = radar.sweep_end_ray_index['data'][max_sweep]
        heights[i+1:,:] = np.ma.masked

    # Mask radar range gates outside specified range
    if min_range is not None:
        i = np.abs(radar.range['data'] / 1000.0 - min_range).argmin()
        heights[:,:i+1] = np.ma.masked
    if max_range is not None:
        i = np.abs(radar.range['data'] / 1000.0 - max_range).argmin()
        heights[:,i+1:] = np.ma.masked

    # Mask incoherent echoes
    if min_ncp is not None:
        ncp = radar.fields[ncp_field]['data']
        heights = np.ma.masked_where(ncp < min_ncp, heights)

    # Mask excluded gates
    if gatefilter is not None:
        heights = np.ma.masked_where(gatefilter.gate_excluded, heights)

    # Parse histogram parameters
    bins = hist_dict['number of bins']
    limits = hist_dict['limits']

    # Compute histogram counts
    counts, bin_edges = np.histogram(
        heights.compressed(), bins=bins, range=limits, normed=False,
        weights=None, density=False)
    hist_dict['histogram counts'] += counts

    # Parse bin edges and bin centers
    bin_centers = bin_edges[:-1] + np.diff(bin_edges) / 2.0
    hist_dict['bin edges'] = bin_edges
    hist_dict['bin centers'] = bin_centers

    return