Example #1
0
def get_radolan_coords(lon, lat, trig=False):
    """
    Calculates x,y coordinates of radolan grid from lon, lat

    Parameters
    ----------

    lon :   float, :class:`numpy:numpy.ndarray` of floats
        longitude
    lat :   float, :class:`numpy:numpy.ndarray` of floats
        latitude
    trig : boolean
        if True, uses trigonometric formulas for calculation,
        otherwise osr transformations
        if False, uses osr spatial reference system to transform
        between projections
        `trig` is recommended to be False, however, the two ways of
        computation are expected to be equivalent.
    """

    if trig:
        # calculation of x_0 and y_0 coordinates of radolan grid
        # as described in the format description
        phi_0 = np.radians(60)
        phi_m = np.radians(lat)
        lam_0 = 10
        lam_m = lon
        lam = np.radians(lam_m - lam_0)
        er = 6370.040
        m_phi = (1 + np.sin(phi_0)) / (1 + np.sin(phi_m))
        x = er * m_phi * np.cos(phi_m) * np.sin(lam)
        y = -er * m_phi * np.cos(phi_m) * np.cos(lam)
    else:
        # create radolan projection osr object
        proj_stereo = projection.create_osr("dwd-radolan")

        # create wgs84 projection osr object
        proj_wgs = projection.get_default_projection()

        x, y = projection.reproject(lon,
                                    lat,
                                    projection_source=proj_wgs,
                                    projection_target=proj_stereo)

    return x, y
Example #2
0
def get_radolan_grid(nrows=None, ncols=None, trig=False, wgs84=False):
    """Calculates x/y coordinates of radolan grid of the German Weather Service

    Returns the x,y coordinates of the radolan grid positions
    (lower left corner of every pixel). The radolan grid is a
    polarstereographic projection, the projection information was taken from
    RADOLAN-RADVOR-OP Kompositformat_2.2.2  :cite:`DWD2009`

    .. table:: Coordinates for 900km x 900km grid

        +------------+-----------+------------+-----------+-----------+
        | Coordinate |   lon     |     lat    |     x     |     y     |
        +============+===========+============+===========+===========+
        | LowerLeft  |  3.5889E  |  46.9526N  | -523.4622 | -4658.645 |
        +------------+-----------+------------+-----------+-----------+
        | LowerRight | 14.6209E  |  47.0705N  |  376.5378 | -4658.645 |
        +------------+-----------+------------+-----------+-----------+
        | UpperRight | 15.7208E  |  54.7405N  |  376.5378 | -3758.645 |
        +------------+-----------+------------+-----------+-----------+
        | UpperLeft  |  2.0715E  |  54.5877N  | -523.4622 | -3758.645 |
        +------------+-----------+------------+-----------+-----------+

    .. table:: Coordinates for 1100km x 900km grid

        +------------+-----------+------------+-----------+-----------+
        | Coordinate |   lon     |     lat    |     x     |     y     |
        +============+===========+============+===========+===========+
        | LowerLeft  |  4.6759E  |  46.1929N  | -443.4622 | -4758.645 |
        +------------+-----------+------------+-----------+-----------+
        | LowerRight | 15.4801E  |  46.1827N  |  456.5378 | -4758.645 |
        +------------+-----------+------------+-----------+-----------+
        | UpperRight | 17.1128E  |  55.5342N  |  456.5378 | -3658.645 |
        +------------+-----------+------------+-----------+-----------+
        | UpperLeft  |  3.0889E  |  55.5482N  | -433.4622 | -3658.645 |
        +------------+-----------+------------+-----------+-----------+

    .. table:: Coordinates for 1500km x 1400km grid

        +------------+-----------+------------+-----------+-----------+
        | Coordinate |   lon     |     lat    |     x     |     y     |
        +============+===========+============+===========+===========+
        | LowerLeft  |  2.3419E  |  43.9336N  | -673.4622 | -5008.645 |
        +------------+-----------+------------+-----------+-----------+

    Parameters
    ----------
    nrows : int
        number of rows (460, 900 by default, 1100, 1500)
    ncols : int
        number of columns (460, 900 by default, 1400)
    trig : boolean
        if True, uses trigonometric formulas for calculation
        if False, uses osr spatial reference system to transform between
        projections
        `trig` is recommended to be False, however, the two ways of computation
        are expected to be equivalent.
    wgs84 : boolean
        if True, output coordinates are in wgs84 lonlat format (default: False)

    Returns
    -------
    radolan_grid : :class:`numpy:numpy.ndarray`
        Array of shape (rows, cols, 2) xy- or lonlat-grid.

    Examples
    --------

    >>> # using osr spatial reference transformation
    >>> import wradlib.georef as georef  # noqa
    >>> radolan_grid = georef.get_radolan_grid()
    >>> print("{0}, ({1:.4f}, {2:.4f})".format(radolan_grid.shape, *radolan_grid[0,0,:]))  # noqa
    (900, 900, 2), (-523.4622, -4658.6447)

    >>> # using pure trigonometric transformations
    >>> import wradlib.georef as georef
    >>> radolan_grid = georef.get_radolan_grid(trig=True)
    >>> print("{0}, ({1:.4f}, {2:.4f})".format(radolan_grid.shape, *radolan_grid[0,0,:]))  # noqa
    (900, 900, 2), (-523.4622, -4658.6447)

    >>> # using osr spatial reference transformation
    >>> import wradlib.georef as georef
    >>> radolan_grid = georef.get_radolan_grid(1500, 1400)
    >>> print("{0}, ({1:.4f}, {2:.4f})".format(radolan_grid.shape, *radolan_grid[0,0,:]))  # noqa
    (1500, 1400, 2), (-673.4622, -5008.6447)

    >>> # using osr spatial reference transformation
    >>> import wradlib.georef as georef
    >>> radolan_grid = georef.get_radolan_grid(900, 900, wgs84=True)
    >>> print("{0}, ({1:.4f}, {2:.4f})".format(radolan_grid.shape, *radolan_grid[0,0,:]))  # noqa
    (900, 900, 2), (3.5889, 46.9526)

    See :ref:`/notebooks/radolan/radolan_grid.ipynb#\
Polar-Stereographic-Projection`.

    Raises
    ------
        TypeError, ValueError
    """

    # setup default parameters in dicts
    tiny = {"j_0": 450, "i_0": 450, "res": 2}
    small = {"j_0": 460, "i_0": 460, "res": 2}
    normal = {"j_0": 450, "i_0": 450, "res": 1}
    normal_wx = {"j_0": 370, "i_0": 550, "res": 1}
    extended = {"j_0": 600, "i_0": 800, "res": 1}
    griddefs = {
        (450, 450): tiny,
        (460, 460): small,
        (900, 900): normal,
        (1100, 900): normal_wx,
        (1500, 1400): extended,
    }

    # type and value checking
    if nrows and ncols:
        if not (isinstance(nrows, int) and isinstance(ncols, int)):
            raise TypeError(
                "wradlib.georef: Parameter *nrows* " "and *ncols* not integer"
            )
        if (nrows, ncols) not in griddefs.keys():
            raise ValueError(
                "wradlib.georef: Parameter *nrows* " "and *ncols* mismatch."
            )
    else:
        # fallback for call without parameters
        nrows = 900
        ncols = 900

    # tiny, small, normal or extended grid check
    # reference point changes according to radolan composit format
    j_0 = griddefs[(nrows, ncols)]["j_0"]
    i_0 = griddefs[(nrows, ncols)]["i_0"]
    res = griddefs[(nrows, ncols)]["res"]

    x_0, y_0 = get_radolan_coords(9.0, 51.0, trig=trig)

    x_arr = np.arange(x_0 - j_0, x_0 - j_0 + ncols * res, res)
    y_arr = np.arange(y_0 - i_0, y_0 - i_0 + nrows * res, res)
    x, y = np.meshgrid(x_arr, y_arr)

    radolan_grid = np.dstack((x, y))

    if wgs84:

        if trig:
            # inverse projection
            lon0 = 10.0  # central meridian of projection
            lat0 = 60.0  # standard parallel of projection

            sinlat0 = np.sin(np.radians(lat0))

            fac = (6370.040 ** 2.0) * ((1.0 + sinlat0) ** 2.0)
            lon = np.degrees(np.arctan((-x / y))) + lon0
            lat = np.degrees(
                np.arcsin((fac - (x ** 2.0 + y ** 2.0)) / (fac + (x ** 2.0 + y ** 2.0)))
            )
            radolan_grid = np.dstack((lon, lat))
        else:
            # create radolan projection osr object
            proj_stereo = projection.create_osr("dwd-radolan")

            # create wgs84 projection osr object
            proj_wgs = projection.get_default_projection()

            radolan_grid = projection.reproject(
                radolan_grid, projection_source=proj_stereo, projection_target=proj_wgs
            )

    return radolan_grid
Example #3
0
def spherical_to_centroids(r, phi, theta, sitecoords, proj=None):
    """
    Generate 3-D centroids of the radar bins from the sperical
    coordinates (r, phi, theta).

    Both azimuth and range arrays are assumed to be equidistant and to contain
    only unique values. The ranges are assumed to define the exterior
    boundaries of the range bins (thus they must be positive). The angles are
    assumed to describe the pointing direction fo the main beam lobe.

    For further information refer to the documentation of
    :meth:`~wradlib.georef.polar2lonlat`.

    Parameters
    ----------
    r : :class:`numpy:numpy.ndarray`
        Array of ranges [m]; r defines the exterior boundaries of the range
        bins! (not the centroids). Thus, values must be positive!
    phi : :class:`numpy:numpy.ndarray`
        Array of azimuth angles containing values between 0° and 360°.
        The angles are assumed to describe the pointing direction fo the main
        beam lobe!
        The first angle can start at any values, but make sure the array is
        sorted continuously positively clockwise and the angles are
        equidistant. An angle if 0 degree is pointing north.
    theta : float
        Elevation angle of scan
    sitecoords : a sequence of three floats
        the lon/lat/alt coordinates of the radar location
    proj : osr object
        Destination Projection

    Returns
    -------
    output : centroids :class:`numpy:numpy.ndarray`
        A 3-d array of bin centroids with shape(num_rays, num_bins, 3).
        The last dimension carries the xyz-coordinates
        either in `aeqd` or given proj.
    proj : osr object
        only returned if proj is None

    Note
    ----
    Azimuth angles of 360 deg are internally converted to 0 deg.

    """
    # make sure the range and azimuth angles have the right properties
    r, phi = _check_polar_coords(r, phi)

    r = r - 0.5 * _get_range_resolution(r)

    # generate a polar grid and convert to lat/lon
    r, phi = np.meshgrid(r, phi)

    coords, rad = spherical_to_xyz(r, phi, theta, sitecoords, squeeze=True)

    if proj is None:
        return coords, rad
    else:
        return projection.reproject(coords,
                                    projection_source=rad,
                                    projection_target=proj)
Example #4
0
def spherical_to_polyvert(r, phi, theta, sitecoords, proj=None):
    """
    Generate 3-D polygon vertices directly from spherical coordinates
    (r, phi, theta).

    This is an alternative to :func:`~wradlib.georef.centroid_to_polyvert`
    which does not use centroids, but generates the polygon vertices by simply
    connecting the corners of the radar bins.

    Both azimuth and range arrays are assumed to be equidistant and to contain
    only unique values. For further information refer to the documentation of
    :func:`~wradlib.georef.spherical_to_xyz`.

    Parameters
    ----------
    r : :class:`numpy:numpy.ndarray`
        Array of ranges [m]; r defines the exterior boundaries of the range
        bins! (not the centroids). Thus, values must be positive!
    phi : :class:`numpy:numpy.ndarray`
        Array of azimuth angles containing values between 0° and 360°.
        The angles are assumed to describe the pointing direction fo the main
        beam lobe!
        The first angle can start at any values, but make sure the array is
        sorted continuously positively clockwise and the angles are
        equidistant. An angle if 0 degree is pointing north.
    theta : float
        Elevation angle of scan
    sitecoords : a sequence of three floats
        the lon/lat/alt coordinates of the radar location
    proj : osr object
        Destination Projection

    Returns
    -------
    output : :class:`numpy:numpy.ndarray`
        A 3-d array of polygon vertices with shape(num_vertices,
        num_vertex_nodes, 2). The last dimension carries the xyz-coordinates
        either in `aeqd` or given proj.
    proj : osr object
        only returned if proj is None

    Examples
    --------
    >>> import wradlib.georef as georef  # noqa
    >>> import numpy as np
    >>> from matplotlib import collections
    >>> import matplotlib.pyplot as pl
    >>> #pl.interactive(True)
    >>> # define the polar coordinates and the site coordinates in lat/lon
    >>> r = np.array([50., 100., 150., 200.]) * 1000
    >>> # _check_polar_coords fails in next line
    >>> # az = np.array([0., 45., 90., 135., 180., 225., 270., 315., 360.])
    >>> az = np.array([0., 45., 90., 135., 180., 225., 270., 315.])
    >>> el = 1.0
    >>> sitecoords = (9.0, 48.0, 0)
    >>> polygons, proj = georef.spherical_to_polyvert(r, az, el, sitecoords)
    >>> # plot the resulting mesh
    >>> fig = pl.figure()
    >>> ax = fig.add_subplot(111)
    >>> #polycoll = mpl.collections.PolyCollection(vertices,closed=True, facecolors=None)  # noqa
    >>> polycoll = collections.PolyCollection(polygons[...,:2], closed=True, facecolors='None')  # noqa
    >>> ret = ax.add_collection(polycoll, autolim=True)
    >>> pl.autoscale()
    >>> pl.show()

    """
    # prepare the range and azimuth array so they describe the boundaries of
    # a bin, not the centroid
    r, phi = _check_polar_coords(r, phi)
    r = np.insert(r, 0, r[0] - _get_range_resolution(r))
    phi = phi - 0.5 * _get_azimuth_resolution(phi)
    phi = np.append(phi, phi[0])
    phi = np.where(phi < 0, phi + 360., phi)

    # generate a grid of polar coordinates of bin corners
    r, phi = np.meshgrid(r, phi)

    coords, rad = spherical_to_xyz(r,
                                   phi,
                                   theta,
                                   sitecoords,
                                   squeeze=True,
                                   strict_dims=True)
    if proj is not None:
        coords = projection.reproject(coords,
                                      projection_source=rad,
                                      projection_target=proj)

    llc = coords[:-1, :-1]
    ulc = coords[:-1, 1:]
    urc = coords[1:, 1:]
    lrc = coords[1:, :-1]

    vertices = np.stack((llc, ulc, urc, lrc, llc), axis=-2).reshape((-1, 5, 3))

    if proj is None:
        return vertices, rad
    else:
        return vertices
Example #5
0
def spherical_to_proj(r,
                      phi,
                      theta,
                      sitecoords,
                      proj=None,
                      re=None,
                      ke=4. / 3.):
    """Transforms spherical coordinates (r, phi, theta) to projected
    coordinates centered at sitecoords in given projection.

    It takes the shortening of the great circle
    distance with increasing elevation angle as well as the resulting
    increase in height into account.

    Parameters
    ----------
    r : :class:`numpy:numpy.ndarray`
        Contains the radial distances.
    phi : :class:`numpy:numpy.ndarray`
        Contains the azimuthal angles.
    theta: :class:`numpy:numpy.ndarray`
        Contains the elevation angles.
    sitecoords : a sequence of three floats
        the lon / lat coordinates of the radar location and its altitude
        a.m.s.l. (in meters)
        if sitecoords is of length two, altitude is assumed to be zero
    proj : osr object
        Destination Spatial Reference System (Projection).
        Defaults to wgs84 (epsg 4326).
    re : float
        earth's radius [m]
    ke : float
        adjustment factor to account for the refractivity gradient that
        affects radar beam propagation. In principle this is wavelength-
        dependent. The default of 4/3 is a good approximation for most
        weather radar wavelengths.

    Returns
    -------
    coords : :class:`numpy:numpy.ndarray`
        Array of shape (..., 3). Contains projected map coordinates.

    Examples
    --------

    A few standard directions (North, South, North, East, South, West) with
    different distances (amounting to roughly 1°) from a site
    located at 48°N 9°E

    >>> r  = np.array([0.,   0., 111., 111., 111., 111.,])*1000
    >>> az = np.array([0., 180.,   0.,  90., 180., 270.,])
    >>> th = np.array([0.,   0.,   0.,   0.,   0.,  0.5,])
    >>> csite = (9.0, 48.0)
    >>> coords = spherical_to_proj(r, az, th, csite)
    >>> for coord in coords:
    ...     print( '{0:7.4f}, {1:7.4f}, {2:7.4f}'.format(*coord))
    ...
     9.0000, 48.0000,  0.0000
     9.0000, 48.0000,  0.0000
     9.0000, 48.9981, 725.7160
    10.4872, 47.9904, 725.7160
     9.0000, 47.0017, 725.7160
     7.5131, 47.9904, 1694.2234

    Here, the coordinates of the east and west directions won't come to lie on
    the latitude of the site because the beam doesn't travel along the latitude
    circle but along a great circle.

    See :ref:`/notebooks/basics/wradlib_workflow.ipynb#\
Georeferencing-and-Projection`.
    """
    if proj is None:
        proj = projection.get_default_projection()

    xyz, rad = spherical_to_xyz(r,
                                phi,
                                theta,
                                sitecoords,
                                re=re,
                                ke=ke,
                                squeeze=True)

    # reproject aeqd to destination projection
    coords = projection.reproject(xyz,
                                  projection_source=rad,
                                  projection_target=proj)

    return coords
Example #6
0
def reproject_raster_dataset(src_ds, **kwargs):
    """Reproject/Resample given dataset according to keyword arguments

    Parameters
    ----------
    src_ds : gdal.Dataset
        raster image with georeferencing (GeoTransform at least)

    Keyword Arguments
    -----------------
    spacing : float
        float or tuple of two floats
        pixel spacing of destination dataset, same unit as pixel coordinates
    size : int
        tuple of two ints
        X/YRasterSize of destination dataset
    resample : GDALResampleAlg
        defaults to GRA_Bilinear
        GRA_NearestNeighbour = 0, GRA_Bilinear = 1, GRA_Cubic = 2,
        GRA_CubicSpline = 3, GRA_Lanczos = 4, GRA_Average = 5, GRA_Mode = 6,
        GRA_Max = 8, GRA_Min = 9, GRA_Med = 10, GRA_Q1 = 11, GRA_Q3 = 12
    projection_target : osr object
        destination dataset projection, defaults to None
    align : bool or Point
        If False, there is no destination grid aligment.
        If True, aligns the destination grid to the next integer multiple of
        destination grid.
        If Point (tuple, list of upper-left x,y-coordinate), the destination
        grid is aligned to this point.

    Returns
    -------
    dst_ds : gdal.Dataset
        reprojected/resampled raster dataset
    """

    # checking kwargs
    spacing = kwargs.pop('spacing', None)
    size = kwargs.pop('size', None)
    resample = kwargs.pop('resample', gdal.GRA_Bilinear)
    src_srs = kwargs.pop('projection_source', None)
    dst_srs = kwargs.pop('projection_target', None)
    align = kwargs.pop('align', False)

    # Get the GeoTransform vector
    src_geo = src_ds.GetGeoTransform()
    x_size = src_ds.RasterXSize
    y_size = src_ds.RasterYSize

    # get extent
    ulx = src_geo[0]
    uly = src_geo[3]
    lrx = src_geo[0] + src_geo[1] * x_size
    lry = src_geo[3] + src_geo[5] * y_size

    extent = np.array([[[ulx, uly], [lrx, uly]], [[ulx, lry], [lrx, lry]]])

    if dst_srs:
        src_srs = osr.SpatialReference()
        src_srs.ImportFromWkt(src_ds.GetProjection())

        # Transformation
        extent = projection.reproject(extent,
                                      projection_source=src_srs,
                                      projection_target=dst_srs)

        # wkt needed
        src_srs = src_srs.ExportToWkt()
        dst_srs = dst_srs.ExportToWkt()

    (ulx, uly, urx, ury, llx, lly, lrx,
     lry) = tuple(list(extent.flatten().tolist()))

    # align grid to destination raster or UL-corner point
    if align:
        try:
            ulx, uly = align
        except TypeError:
            pass

        ulx = int(max(np.floor(ulx), np.floor(llx)))
        uly = int(min(np.ceil(uly), np.ceil(ury)))
        lrx = int(min(np.ceil(lrx), np.ceil(urx)))
        lry = int(max(np.floor(lry), np.floor(lly)))

    # calculate cols/rows or xspacing/yspacing
    if spacing:
        try:
            x_ps, y_ps = spacing
        except TypeError:
            x_ps = spacing
            y_ps = spacing

        cols = int(abs(lrx - ulx) / x_ps)
        rows = int(abs(uly - lry) / y_ps)
    elif size:
        cols, rows = size
        x_ps = x_size * src_geo[1] / cols
        y_ps = y_size * abs(src_geo[5]) / rows
    else:
        raise NameError("Whether keyword 'spacing' or 'size' must be given")

    # create destination in-memory raster
    mem_drv = gdal.GetDriverByName('MEM')

    # and set RasterSize according ro cols/rows
    dst_ds = mem_drv.Create('', cols, rows, 1, gdal.GDT_Float32)

    # Create the destination GeoTransform with changed x/y spacing
    dst_geo = (ulx, x_ps, src_geo[2], uly, src_geo[4], -y_ps)

    # apply GeoTransform to destination dataset
    dst_ds.SetGeoTransform(dst_geo)

    # apply Projection to destination dataset
    if dst_srs is not None:
        dst_ds.SetProjection(dst_srs)

    # nodata handling, need to initialize dst_ds with nodata
    src_band = src_ds.GetRasterBand(1)
    nodata = src_band.GetNoDataValue()
    dst_band = dst_ds.GetRasterBand(1)
    if nodata is not None:
        dst_band.SetNoDataValue(nodata)
        dst_band.WriteArray(np.ones((rows, cols)) * nodata)
    dst_band.FlushCache()

    # resample and reproject dataset
    gdal.ReprojectImage(src_ds, dst_ds, src_srs, dst_srs, resample)

    return dst_ds