Beispiel #1
0
def volcoords_from_polar(sitecoords, elevs, azimuths, ranges, proj=None):
    """Create Cartesian coordinates for regular polar volumes

    Parameters
    ----------
    sitecoords : sequence of three floats indicating the radar position
        (longitude in decimal degrees, latitude in decimal degrees,
        height a.s.l. in meters)
    elevs : sequence of elevation angles
    azimuths : sequence of azimuth angles
    ranges : sequence of ranges
    proj : osr spatial reference object
        GDAL OSR Spatial Reference Object describing projection

    Returns
    -------
    output :  :class:`numpy:numpy.ndarray`
        (num volume bins, 3)

    Examples
    --------
    See :ref:`/notebooks/workflow/recipe2.ipynb`.
    """
    # make sure that elevs is an array
    elevs = np.array([elevs]).ravel()
    # create polar grid
    el, az, r = util.meshgrid_n(elevs, azimuths, ranges)

    # get projected coordinates
    coords = georef.spherical_to_proj(r, az, el, sitecoords, proj=proj)
    coords = coords.reshape(-1, 3)

    return coords
Beispiel #2
0
def plot_ppi_crosshair(site,
                       ranges,
                       angles=None,
                       proj=None,
                       elev=0.,
                       ax=None,
                       **kwargs):
    """Plots a Crosshair for a Plan Position Indicator (PPI).

    Parameters
    ----------
    site : tuple
        Tuple of coordinates of the radar site.
        If `proj` is not used, this simply becomes the offset for the origin
        of the coordinate system.
        If `proj` is used, values must be given as (longitude, latitude,
        altitude) tuple of geographical coordinates.
    ranges : list
        List of ranges, for which range circles should be drawn.
        If ``proj`` is None arbitrary units may be used (such that they fit
        with the underlying PPI plot.
        Otherwise the ranges must be given in meters.
    angles : list
        List of angles (in degrees) for which straight lines should be drawn.
        These lines will be drawn starting from the center and until the
        largest range.
    proj : osr spatial reference object
        GDAL OSR Spatial Reference Object describing projection
        The function will calculate lines and circles according to
        georeferenced coordinates taking beam propagation, earth's curvature
        and scale effects due to projection into account.
        Depending on the projection, crosshair lines might not be straight and
        range circles might appear elliptical (also check if the aspect of the
        axes might not also be responsible for this).
    elev : float or array of same shape as az
        Elevation angle of the scan or individual azimuths.
        May improve georeferencing coordinates for larger elevation angles.
    ax : :class:`matplotlib:matplotlib.axes.Axes`
        If given, the crosshair will be plotted into this axes object. If None
        matplotlib's current axes (function gca()) concept will be used to
        determine the axes.

    Keyword Arguments
    -----------------
    line :  dict
        dictionary, which will be passed to the crosshair line objects using
        the standard keyword inheritance mechanism. If not given defaults will
        be used.
    circle : dict
        dictionary, which will be passed to the range circle line objects using
        the standard keyword inheritance mechanism. If not given defaults will
        be used.

    See also
    --------
    :func:`~wradlib.vis.plot_ppi` - plotting a PPI in cartesian coordinates

    Returns
    -------
    ax :  :class:`matplotlib:matplotlib.axes.Axes`
        The axes object into which the PPI was plotted

    Examples
    --------
    See :ref:`/notebooks/visualisation/wradlib_plot_ppi_example.ipynb`.

    """
    # check coordinate tuple
    if site and len(site) < 3:
        raise ValueError("WRADLIB: `site` need to be a tuple of coordinates "
                         "(longitude, latitude, altitude).")

    # if we didn't get an axes object, find the current one
    if ax is None:
        ax = pl.gca()

    if angles is None:
        angles = [0, 90, 180, 270]

    # set default line keywords
    linekw = dict(color='gray', linestyle='dashed')
    # update with user settings
    linekw.update(kwargs.get('line', {}))

    # set default circle keywords
    circkw = dict(edgecolor='gray', linestyle='dashed', facecolor='none')
    # update with user settings
    circkw.update(kwargs.get('circle', {}))

    # determine coordinates for 'straight' lines
    if proj:
        # projected
        # reproject the site coordinates
        psite = georef.reproject(*site, projection_target=proj)
        # these lines might not be straigt so we approximate them with 10
        # segments. Produce polar coordinates
        rr, az = np.meshgrid(np.linspace(0, ranges[-1], 10), angles)
        # convert from spherical to projection
        coords = georef.spherical_to_proj(rr, az, elev, site, proj=proj)
        nsewx = coords[..., 0]
        nsewy = coords[..., 1]
    else:
        # no projection
        psite = site
        rr, az = np.meshgrid(np.linspace(0, ranges[-1], 2), angles)
        # use simple trigonometry to calculate coordinates
        nsewx, nsewy = (psite[0] + rr * np.cos(np.radians(90 - az)),
                        psite[1] + rr * np.sin(np.radians(90 - az)))

    # mark the site, just in case nothing else would be drawn
    ax.plot(*psite[:2], marker='+', **linekw)

    # draw the lines
    for i in range(len(angles)):
        ax.add_line(lines.Line2D(nsewx[i, :], nsewy[i, :], **linekw))

    # draw the range circles
    if proj:
        # produce an approximation of the circle
        x, y = np.meshgrid(ranges, np.arange(360))
        poly = georef.spherical_to_proj(ranges,
                                        np.arange(360),
                                        elev,
                                        site,
                                        proj=proj)[..., :2]
        poly = np.swapaxes(poly, 0, 1)
        for p in poly:
            ax.add_patch(patches.Polygon(p, **circkw))
    else:
        # in the unprojected case, we may use 'true' circles.
        for r in ranges:
            ax.add_patch(patches.Circle(psite, r, **circkw))

    # there should be not much wrong, setting the axes aspect to equal
    # by default
    ax.set_aspect('equal')

    # return the axes object for later use
    return ax
Beispiel #3
0
 def test_spherical_to_proj(self):
     coords = georef.spherical_to_proj(self.r, self.az,
                                       self.th, self.csite)
     self.assertTrue(np.allclose(coords[..., 0], self.result_n[0]))
     self.assertTrue(np.allclose(coords[..., 1], self.result_n[1]))
     self.assertTrue(np.allclose(coords[..., 2], self.result_n[2]))
Beispiel #4
0
 def test_spherical_to_proj(self):
     coords = georef.spherical_to_proj(self.r, self.az, self.th, self.csite)
     self.assertTrue(np.allclose(coords[..., 0], self.result_n[0]))
     self.assertTrue(np.allclose(coords[..., 1], self.result_n[1]))
     self.assertTrue(np.allclose(coords[..., 2], self.result_n[2]))
Beispiel #5
0
def volcoords_from_polar_irregular(sitecoords, elevs, azimuths,
                                   ranges, proj=None):
    """Create Cartesian coordinates for polar volumes with irregular \
    sweep specifications

    Parameters
    ----------
    sitecoords : sequence of three floats indicating the radar position
        (longitude in decimal degrees, latitude in decimal degrees,
        height a.s.l. in meters)
    elevs : sequence of elevation angles
    azimuths : sequence of azimuth angles
    ranges : sequence of ranges
    proj : object
        GDAL OSR Spatial Reference Object describing projection

    Returns
    -------
    output : :class:`numpy:numpy.ndarray`
        (num volume bins, 3)

    """
    # check structure: Are azimuth angles and range bins the same for each
    # elevation angle?
    oneaz4all = True
    onerange4all = True
    #   check elevs array, first: must be one-dimensional
    try:
        elevs = np.array(elevs)
    except Exception:
        print("Could not create an array from argument <elevs>.")
        print("The following exception was raised:")
        raise
    assert (elevs.ndim == 1) and (elevs.dtype != np.dtype("object")), \
        "Argument <elevs> in wradlib.volcoords_from_polar must be a 1-D array."
    # now: is there one azimuths array for all elevation angles
    # or one for each?
    try:
        azimuths = np.array(azimuths)
    except Exception:
        print("Could not create an array from argument <azimuths>.")
        print("The following exception was raised:")
        raise
    if len(azimuths) == len(elevs):
        # are the items of <azimuths> arrays themselves?
        isseq = [util.issequence(elem) for elem in azimuths]
        assert not ((False in isseq) and (True in isseq)), \
            "Argument <azimuths> contains both iterable " \
            "and non-iterable items."
        if True in isseq:
            # we expect one azimuth array for each elevation angle
            oneaz4all = False
    # now: is there one ranges array for all elevation angles or one for each?
    try:
        ranges = np.array(ranges)
    except Exception:
        print("Could not create an array from argument <ranges>.")
        print("The following exception was raised:")
        raise
    if len(ranges) == len(elevs):
        # are the items of <azimuths> arrays themselves?
        isseq = [util.issequence(elem) for elem in ranges]
        assert not ((False in isseq) and (True in isseq)), \
            "Argument <azimuths> contains both iterable " \
            "and non-iterable items."
        if True in isseq:
            # we expect one azimuth array for each elevation angle
            onerange4all = False
    if oneaz4all and onerange4all:
        # this is the simple way
        return volcoords_from_polar(sitecoords, elevs, azimuths, ranges, proj)
    # No simply way, so we need to construct the coordinates arrays for
    # each elevation angle
    # but first adapt input arrays to this task
    if onerange4all:
        ranges = np.array([ranges for i in range(len(elevs))])
    if oneaz4all:
        azimuths = np.array([azimuths for i in range(len(elevs))])
    # and second create the corresponding polar volume grid
    el = np.array([])
    az = np.array([])
    r = np.array([])
    for i, elev in enumerate(elevs):
        az_tmp, r_tmp = np.meshgrid(azimuths[i], ranges[i])
        el = np.append(el, np.repeat(elev, len(azimuths[i]) * len(ranges[i])))
        az = np.append(az, az_tmp.ravel())
        r = np.append(r, r_tmp.ravel())
    # get projected coordinates
    coords = georef.spherical_to_proj(r, az, el, sitecoords, proj=proj)
    coords = coords.reshape(-1, 3)

    return coords