def test_bin_distance(self): distance = georef.bin_distance(np.arange(10., 101., 10.) * 1000., 2., 0, 6370040.) distref = np.array([9993.49302358, 19986.13717891, 29977.90491409, 39968.76869178, 49958.70098959, 59947.6743006, 69935.66113377, 79922.63401441, 89908.5654846, 99893.4281037]) np.testing.assert_allclose(distref, distance)
def test_bin_distance(self): distance = georef.bin_distance(np.arange(10., 101., 10.) * 1000., 2., 0, 6370040.) distref = np.array([9993.49302358, 19986.13717891, 29977.90491409, 39968.76869178, 49958.70098959, 59947.6743006, 69935.66113377, 79922.63401441, 89908.5654846, 99893.4281037]) np.testing.assert_allclose(distref, distance)
def plot_scan_strategy( ranges, elevs, sitecoords, beamwidth=1.0, vert_res=500.0, maxalt=10000.0, range_res=None, maxrange=None, units="m", terrain=None, az=0.0, cg=False, ax=111, cmap="tab10", ): """Plot the vertical scanning strategy. Parameters ---------- ranges : sequence of floats array of ranges elevs : sequence of floats elevation angles sitecoords : sequence of floats radar site coordinates (longitude, latitude, altitude) beamwidth : float 3dB width of the radar beam, defaults to 1.0 deg. vert_res : float Vertical resolution in [m]. maxalt : float Maximum altitude in [m]. range_res : float Horizontal resolution in [m]. maxrange : float Maximum range in [m]. units : str Units to plot in, can be 'm' or 'km'. Defaults to 'm'. terrain : bool | :class:`numpy:numpy.ndarray` If True, downloads srtm data and add orography for given `az`. az : float Used to specify azimuth for terrain plots. cg : bool If True, plot in curvelinear grid, defaults to False (cartesian grid). ax : :class:`matplotlib:matplotlib.axes.Axes` | matplotlib grid definition If matplotlib Axes object is given, the scan strategy will be plotted into this axes object. If matplotlib grid definition is given (nrows/ncols/plotnumber), axis are created in the specified place. Defaults to '111', only one subplot/axis. cmap : str matplotlib colormap string. Returns ------- ax : :class:`matplotlib:matplotlib.axes.Axes` | matplotlib toolkit axisartist Axes object matplotlib Axes or curvelinear Axes (r-theta-grid) depending on keyword argument `cg`. """ if units == "m": scale = 1.0 elif units == "km": scale = 1000.0 else: raise ValueError( f"wradlib: unknown value for keyword argument units={units}") az = np.array([az]) if maxrange is None: maxrange = ranges.max() xyz, rad = georef.spherical_to_xyz(ranges, az, elevs, sitecoords, squeeze=True) add_title = "" if terrain is True: add_title += f" - Azimuth {az[0]}°" ll = georef.reproject(xyz, projection_source=rad) # (down-)load srtm data ds = io.get_srtm( [ ll[..., 0].min(), ll[..., 0].max(), ll[..., 1].min(), ll[..., 1].max() ], download={}, ) rastervalues, rastercoords, proj = georef.extract_raster_dataset( ds, nodata=-32768.0) # map rastervalues to polar grid points terrain = ipol.cart_to_irregular_spline(rastercoords, rastervalues, ll[-1, ..., :2], order=3, prefilter=False) if ax == 111: fig = pl.figure(figsize=(16, 8)) else: fig = pl.gcf() legend2 = {} if cg is True: ax, caax, paax = create_cg(fig=fig, subplot=ax, rot=0, scale=1) # for nice plotting we assume earth_radius = 6370000 m er = 6370000 # calculate beam_height and arc_distance for ke=1 # means line of sight ade = georef.bin_distance(ranges, 0, sitecoords[2], re=er, ke=1.0) nn0 = np.zeros_like(ranges) ecp = nn0 + er # theta (arc_distance sector angle) thetap = -np.degrees(ade / er) + 90.0 # zero degree elevation with standard refraction (bes, ) = paax.plot(thetap, ecp, "-k", linewidth=3, label="_MSL", zorder=3) legend2["MSL"] = bes if terrain is not None: paax.fill_between(thetap, ecp.min() - 2500, ecp + terrain, color="0.75", zorder=2) # axes layout ax.set_xlim(0, np.max(ade)) ax.set_ylim([ecp.min() - maxalt / 5, ecp.max() + maxalt]) caax.grid(True, axis="x") ax.grid(True, axis="y") ax.axis["top"].toggle(all=False) gh = ax.get_grid_helper() yrange = maxalt + maxalt / 5 nbins = ((yrange // vert_res) * 2 + 1) // np.sqrt(2) gh.grid_finder.grid_locator2._nbins = nbins else: ax = fig.add_subplot(ax) paax = ax caax = ax if terrain is not None: paax.fill_between(ranges, 0, terrain, color="0.75", zorder=2) ax.set_xlim(0.0, maxrange) ax.set_ylim(0.0, maxalt) ax.grid() # axes ticks and formatting if range_res is not None: xloc = range_res caax.xaxis.set_major_locator(MultipleLocator(xloc)) else: caax.xaxis.set_major_locator(MaxNLocator()) yloc = vert_res caax.yaxis.set_major_locator(MultipleLocator(yloc)) import functools hform = functools.partial(_height_formatter, cg=cg, scale=scale) rform = functools.partial(_range_formatter, scale=scale) caax.yaxis.set_major_formatter(FuncFormatter(hform)) caax.xaxis.set_major_formatter(FuncFormatter(rform)) # color management from cycler import cycler NUM_COLORS = len(elevs) cmap = pl.get_cmap(cmap) if cmap.N >= 256: colors = [cmap(1.0 * i / NUM_COLORS) for i in range(NUM_COLORS)] else: colors = cmap.colors cycle = cycler(color=colors) paax.set_prop_cycle(cycle) # plot beams for i, el in enumerate(elevs): alt = xyz[i, ..., 2] groundrange = np.sqrt(xyz[i, ..., 0]**2 + xyz[i, ..., 1]**2) if cg: plrange = thetap plalt = ecp + alt beamradius = util.half_power_radius(ranges, beamwidth) else: plrange = np.insert(groundrange, 0, 0) plalt = np.insert(alt, 0, sitecoords[2]) beamradius = util.half_power_radius(plrange, beamwidth) _, center, edge = _plot_beam(plrange, plalt, beamradius, label=f"{el:4.1f}°", ax=paax) # legend 1 handles, labels = ax.get_legend_handles_labels() leg1 = ax.legend( handles, labels, prop={"family": "monospace"}, loc="upper left", bbox_to_anchor=(1.04, 1), borderaxespad=0, ) # legend 2 legend2["Center"] = center[0] legend2["3 dB"] = edge[0] ax.legend( legend2.values(), legend2.keys(), prop={"family": "monospace"}, loc="lower left", bbox_to_anchor=(1.04, 0), borderaxespad=0, ) # add legend 1 ax.add_artist(leg1) # set axes labels ax.set_title(f"Radar Scan Strategy - {sitecoords}" + add_title) caax.set_xlabel(f"Range ({units})") caax.set_ylabel(f"Altitude ({units})") return ax