def test_get_aacgm_coord_datetime_date(self): """Test single AACGMV2 calculation with date and datetime input""" (self.mlat_out, self.mlon_out, self.mlt_out) = aacgmv2.get_aacgm_coord(60, 0, 300, self.ddate) mlat_2, mlon_2, mlt_2 = aacgmv2.get_aacgm_coord(60, 0, 300, self.dtime) np.testing.assert_almost_equal(self.mlat_out, mlat_2, decimal=6) np.testing.assert_almost_equal(self.mlon_out, mlon_2, decimal=6) np.testing.assert_almost_equal(self.mlt_out, mlt_2, decimal=6) del mlat_2, mlon_2, mlt_2
def geomag_lat(alt, start_time, conv_module): ''' Calculates the geomagnetic lattitude data at the given alt (altitude) and start_time. This function uses either Spacepy or aacgmv2 to convert between geographic and geomagnetic coordinates. The moduel used to convert coordinates can be selected by setting the conv_module. Make sure to use all lowercase for converting module. Keep in mind SpacePy for some reason is not able to use 2020 data. ''' arr = np.zeros((181, 360)) geo_lat = np.zeros((5, 360)) for j in range(360): for i in range(181): # Altitude is given in meters but Spacepy uses kilometers. coordinates = coords.Coords([alt / 1000, i - 90, j - 180], \ 'GEO', 'sph') # For some reason, 2020 data could not be used. if conv_module == 'spacepy': coordinates.ticks = Ticktock(['2019-07-17T17:51:15'], 'ISO') arr[i][j] = coordinates.convert('MAG', 'sph').lati elif conv_module == 'aacgmv2': arr[i][j] = (np.array(aacgmv2.get_aacgm_coord(i - 90, j - 180,\ int(alt / 1000), start_time)))[0] else: print("Error: coordinate conversion module is invalid.\n\ Please choose between:\n spacepy\n aacgmv2\n\ Please use all lowercase.") exit() for j in range(360): for i in range(5): geo_lat[i, j] = closest(arr[:, j], 30 * i - 60) - 90 return geo_lat
def test_get_aacgm_coord_location_failure(self): """Test single value AACGMV2 calculation with a bad location""" (self.mlat_out, self.mlon_out, self.mlt_out) = aacgmv2.get_aacgm_coord(0, 0, 0, self.dtime) if not (np.isnan(self.mlat_out) & np.isnan(self.mlon_out) & np.isnan(self.mlt_out)): raise AssertionError()
def test_get_aacgm_coord_location_failure(self): """Test single value AACGMV2 calculation with a bad location""" self.in_args.extend([0.0, self.dtime, 'TRACE']) self.in_args[0] = 0.0 self.out = aacgmv2.get_aacgm_coord(*self.in_args) np.all(np.isnan(np.array(self.out)))
def aacgm_coordinates(stid: int, beams: int = None, gates: tuple = None, date: dt.datetime = dt.datetime.now, **kwargs): if gates is None: gates = [0, SuperDARNRadars.radars[stid].range_gate_45] if beams is None: beams = SuperDARNRadars.radars[stid].hardware_info.beams # Plus 1 is due to the fact fov files index at 1 so in the plotting # of the boundary there is a subtraction of 1 to offset this as python # converts to index of 0 which my code already accounts for beam_corners_lats = np.zeros((gates[1]-gates[0]+1, beams+1)) beam_corners_lons = np.zeros((gates[1]-gates[0]+1, beams+1)) for beam in range(0, beams+1): for gate in range(gates[0], gates[1]+1): lat, lon = gate2geographic_location(stid=stid, beam=beam, range_gate=gate, height=300, **kwargs) geomag = np.array(aacgmv2.get_aacgm_coord(glat=lat, glon=lon, height=250, dtime=date)) beam_corners_lats[gate-gates[0], beam] = geomag[0] beam_corners_lons[gate-gates[0], beam] = geomag[1] y0inx = np.min(np.where(np.isfinite(beam_corners_lats[:,0]))[0]) return beam_corners_lats[y0inx:], beam_corners_lons[y0inx:]
def get_aacgm_geom(self, feature, out_height=300.): new_i = [] # cartopy.feature.COASTLINE for _n, i in enumerate(feature.geometries()): aa = mapping(i) mag_list = [] geo_coords = aa["coordinates"] for _ni, _list in enumerate(geo_coords): mlon_check_jump_list = [] split_mag_list = None if len(_list) == 1: _loop_list = _list[0] else: _loop_list = _list for _ngc, _gc in enumerate(_loop_list): _mc = aacgmv2.get_aacgm_coord(_gc[1], _gc[0], out_height, self.plot_date) if numpy.isnan(_mc[0]): continue mlon_check_jump_list.append(_mc[1]) if self.coords == "aacgmv2": mag_list.append((_mc[1], _mc[0])) else: if _mc[2] * 15. > 180.: mag_list.append((_mc[2] * 15. - 360., _mc[0])) else: mag_list.append((_mc[2] * 15., _mc[0])) # check for unwanted jumps mlon_check_jump_list = numpy.array(mlon_check_jump_list) jump_arr = numpy.diff(mlon_check_jump_list) bad_inds = numpy.where(numpy.abs(jump_arr) > 10.)[0] # delete the range of bad values # This is further complicated because # in some locations mlon jumps from -177 to +178 # and this causes jumps in the maps! To deal with # this we'll split arrays of such jumps # (these jumps typically have just one bad ind ) # and make them into two seperate entities (LineStrings) # so that shapely will treat them as two seperate boundaries! if len(bad_inds) > 0: if len(bad_inds) > 1: mag_list = [ i for j, i in enumerate(mag_list) if j - 1 not in numpy.arange(bad_inds[0], bad_inds[1]) ] else: split_mag_list = mag_list[bad_inds[0] + 1:] mag_list = mag_list[:bad_inds[0] + 1] mag_coords = tuple(mag_list) if len(mag_list) > 1: new_i.append(mag_coords) if split_mag_list is not None: # print(split_mag_list) if len(split_mag_list) > 1: new_i.append(tuple(split_mag_list)) aacgm_coast = MultiLineString(new_i) return aacgm_coast
def test_get_aacgm_coord(self): """Test single value AACGMV2 calculation, defaults to TRACE""" (self.mlat_out, self.mlon_out, self.mlt_out) = aacgmv2.get_aacgm_coord(60, 0, 300, self.dtime) np.testing.assert_almost_equal(self.mlat_out, 58.2247, decimal=4) np.testing.assert_almost_equal(self.mlon_out, 81.1761, decimal=4) np.testing.assert_almost_equal(self.mlt_out, 0.1889, decimal=4)
def plot_radar_label(cls, stid: int, date: dt.datetime, coords: Coords = Coords.AACGM_MLT, projs: Projs = Projs.POLAR, line_color: str = 'black', transform: object = None, **kwargs): """ plots only string at the position of a given radar station ID (stid) Parameters ----------- stid: int Radar station ID date: datetime datetime object sets the datetime used to find the coordinates of the FOV line_color: str color of the text default: black Returns ------- No variables returned """ # Label text label_str = ' ' + SuperDARNRadars.radars[stid]\ .hardware_info.abbrev.upper() # Get location of radar lat = SuperDARNRadars.radars[stid].hardware_info.geographic.lat lon = SuperDARNRadars.radars[stid].hardware_info.geographic.lon # Convert to geomag coords if coords == Coords.AACGM_MLT or coords == Coords.AACGM: geomag_radar = aacgmv2.get_aacgm_coord(lat, lon, 250, date) lat = geomag_radar[0] lon = geomag_radar[1] if coords == Coords.AACGM_MLT: mltshift = geomag_radar[1] -\ (aacgmv2.convert_mlt(geomag_radar[1], date) * 15) lon = geomag_radar[1] - mltshift if projs == Projs.POLAR: lon = np.radians(lon) theta_text = lon # Shift in latitude (dependent on hemisphere) if SuperDARNRadars.radars[stid].hemisphere == Hemisphere.North: r_text = lat - 5 else: r_text = lat + 5 plt.text(theta_text, r_text, label_str, ha='center', transform=transform, c=line_color) return
def calculate_magnetic_latitude(glon, glat, dtime): for height in arange(0, 100, 0.5): calclat, calclon, z = aacgmv2.get_aacgm_coord(glat, glon, height, start) if np.isnan(calclat) != True: return calclat, calclon, height return nan, nan, nan
def print_mag_table(st, table): gm = geomag.geomag.GeoMag() print "===== MAG =====" print "Time (UTC), geodetic latitude (degrees), geodetic longitude (degrees), alt (km above WGS-84 ellipsoid), magnetic declination (degrees), inclination (degrees), total intensity (nT), horizontal (nT), north (nT), east (nT), vertical (nT), magnetic latitude (degrees), magnetic longitude (degrees), magnetic local time (hours)" for i in range(0, len(table)): mag = gm.GeoMag(table[i][2], table[i][1], table[i][3]*KM_TO_FEET, datetime.date(table[i][0])) aacgm = aacgmv2.get_aacgm_coord(table[i][2], table[i][1], table[i][3], table[i][0]) print "%s, %6.2f, %8.2f, %9.2f, %6.2f, %8.2f, %7.1f, %7.1f, %7.1f, %7.1f, %7.1f, %6.2f, %8.2f, %5.2f" % \ (table[i][0], table[i][2], table[i][1], table[i][3], mag.dec, mag.dip, mag.ti, mag.bh, mag.bx, mag.by, mag.bz, aacgm[0], aacgm[1], aacgm[2])
def test_get_aacgm_coord_maxalt_failure(self): """For a single value, test failure for an altitude too high for coefficients""" method = "" (self.mlat_out, self.mlon_out, self.mlt_out) = aacgmv2.get_aacgm_coord(60, 0, 2001, self.dtime, method=method) if not (np.isnan(self.mlat_out) & np.isnan(self.mlon_out) & np.isnan(self.mlt_out)): raise AssertionError()
def print_mag(st, llap): print("===== MAG =====") print("Date/Time: %s Satellite Number: %s" % \ (llap[0], st.get_satellite_number())) d = datetime.date(llap[0]) gm = geomag.geomag.GeoMag() mag = gm.GeoMag(llap[2], llap[1], llap[3] * KM_TO_FEET, d) aacgm = aacgmv2.get_aacgm_coord(llap[2], llap[1], llap[3], llap[0]) print("Geodetic Latitude (degrees)/Geodetic Longitude (degrees)/Altitude (km above WGS-84 ellipsoid)/Declination (degrees)/Inclination (degrees)/Total Intensity (nT)/Horizontal (nT)/North (nT)/East (nT)/Vertical (nT)/Magnetic Latitude (degrees)/Magnetic Longitude(degrees)/Magnetic Local Time (hours): %s/%s/%s/%s/%s/%s/%s/%s/%s/%s/%s/%s/%s" % \ (llap[2], llap[1], llap[3], mag.dec, mag.dip, mag.ti, mag.bh, mag.bx, mag.by, mag.bz, aacgm[0], aacgm[1], aacgm[2]))
def test_get_aacgm_coord_badidea(self): """Test single value AACGMV2 calculation with a bad flag""" method = "BADIDEA" (self.mlat_out, self.mlon_out, self.mlt_out) = aacgmv2.get_aacgm_coord(60, 0, 3000, self.dtime, method=method) np.testing.assert_almost_equal(self.mlat_out, 64.3568, decimal=4) np.testing.assert_almost_equal(self.mlon_out, 83.3027, decimal=4) np.testing.assert_almost_equal(self.mlt_out, 0.3307, decimal=4) del method
def test_warning_below_ground_get_aacgm_coord(self): """ Test that a warning is issued if altitude is below zero""" import logbook lwarn = u"conversion not intended for altitudes < 0 km" with logbook.TestHandler() as handler: (self.mlat_out, self.mlon_out, self.mlt_out) = aacgmv2.get_aacgm_coord(60, 0, -1, self.dtime) if not handler.has_warning(lwarn): raise AssertionError() handler.close()
def convert(date, time, intfactor, coordsystem, medfilter): directory = str(os.getcwd()) directory = directory + '\\' + str(date.year) + '\\' + str(date.month) + '\\' + str(date.day) with np.load(directory + '\\data.npz') as data: tec = data['tec'] timeint = timeconvert(time) Z = tec[:,:,timeint] Z = interpolate(Z, tec, timeint, intfactor) if medfilter: Z = medianfilter(Z, tec, timeint) lon = np.linspace(-180, 180, 361) lat = np.linspace(-90, 90, 181) lon2d = np.empty((180, 360)) lat2d = np.empty((180, 360)) lon2d[:] = np.nan lat2d[:] = np.nan if(coordsystem == 'AACGMv2'): for i in range(0,179): for j in range(0, 359): lat2d[i,j], lon2d[i,j], x = aacgmv2.get_aacgm_coord(i - 90, j - 180, 350, date) #remove jumps in longitude by rearranging matrix of lon, lat, and Z for row in range(0, 179): for element in range(0,358): if (lon2d[row, element] > 0) and (lon2d[row, element+1] < 0) and (180 < (lon2d[row, element] - lon2d[row, element+1])): first = lon2d[row,:element+1] second = lon2d[row,element+1:] lon2d[row,:] = np.append(second, first) first = lat2d[row,:element+1] second = lat2d[row,element+1:] lat2d[row,:] = np.append(second, first) first = Z[row,:element+1] second = Z[row,element+1:] Z[row,:] = np.append(second, first) return int(lat2d), int(lon2d), Z elif(coordsystem == 'IGRF'): lon = np.linspace(-180, 180, 361) lat = np.linspace(-90, 90, 181) lon2d = np.empty((180, 360)) lat2d = np.empty((180, 360)) lon2d[:] = np.nan lat2d[:] = np.nan for i in range(0,179): for j in range(0, 359): _mc = geotomag(i - 90, j - 180, 350, date) lat2d[i,j] = float(_mc.data[:,1]) lon2d[i,j] = float(_mc.data[:,2]) return lat2d, lon2d, Z else: return lat, lon, Z#Note that return could be 1d or 2d array
def test_get_aacgm_coord_mlat_failure(self): """Test error return for co-latitudes above 90 for a single value""" import logbook lerr = u"unrealistic latitude" with logbook.TestHandler() as hhigh: with pytest.raises(AssertionError): (self.mlat_out, self.mlon_out, self.mlt_out) = aacgmv2.get_aacgm_coord(91, 0, 300, self.dtime) if not hhigh.has_error(lerr): raise AssertionError() with logbook.TestHandler() as hlow: with pytest.raises(AssertionError): (self.mlat_out, self.mlon_out, self.mlt_out) = aacgmv2.get_aacgm_coord(-91, 0, 300, self.dtime) if not hlow.has_error(lerr): raise AssertionError() hhigh.close() hlow.close()
def plot_radar_position(cls, stid: int, date: dt.datetime, transform: object = None, coords: Coords = Coords.AACGM_MLT, projs: Projs = Projs.POLAR, line_color: str = 'black', **kwargs): """ plots only a dot at the position of a given radar station ID (stid) Parameters ----------- stid: int Radar station ID date: datetime datetime object sets the datetime used to find the coordinates of the FOV line_color: str color of the dot default: black Returns ------- No variables returned """ # Get location of radar lat = SuperDARNRadars.radars[stid].hardware_info.geographic.lat lon = SuperDARNRadars.radars[stid].hardware_info.geographic.lon # Convert to geomag coords if coords == Coords.AACGM_MLT or coords == Coords.AACGM: geomag_radar = aacgmv2.get_aacgm_coord(lat, lon, 250, date) lat = geomag_radar[0] lon = geomag_radar[1] if coords == Coords.AACGM_MLT: mltshift = geomag_radar[1] -\ (aacgmv2.convert_mlt(geomag_radar[1], date) * 15) lon = geomag_radar[1] - mltshift if projs == Projs.POLAR: lon = np.radians(lon) # Plot a dot at the radar site plt.scatter(lon, lat, c=line_color, s=5, transform=transform) return
def test_get_aacgm_coord_time_failure(self): """Test single value AACGMV2 calculation with a bad datetime""" with pytest.raises(AssertionError): (self.mlat_out, self.mlon_out, self.mlt_out) = aacgmv2.get_aacgm_coord(60, 0, 300, None)
def geo_lat(LAT, LON, ALT, start_time): for i in range(120): for j in range(61): print( np.array( aacgmv2.get_aacgm_coord(3 * j - 90, i, ALT, start_time)))
def test_get_aacgm_coord_raise_value_error(self, in_index, value): """Test different ways to raise a ValueError""" self.in_args.extend([300.0, self.dtime]) self.in_args[in_index] = value with pytest.raises(ValueError): self.out = aacgmv2.get_aacgm_coord(*self.in_args)
def test_get_aacgm_coord(self, alt, method_code, ref): """Test single value AACGMV2 calculation, defaults to TRACE""" self.in_args.extend([alt, self.dtime, method_code]) self.out = aacgmv2.get_aacgm_coord(*self.in_args) np.testing.assert_allclose(self.out, ref, rtol=self.rtol)
def radar_fov(stid: int, coords: str = 'aacgm', date: datetime = None): """ Returning beam/gate coordinates of a specified radar's field-of-view Parameters ---------- stid: int Station ID of radar of choice. See 'superdarn.ca/radar-info' for ID numbers. coords: str Type of coordinates returned. 'geo' = Geographic, 'aacgm' = AACGMv2 Default: 'aacgm' date: datetime datetime object date to be used for AACGMv2 conversion Default: Current day Returns ---------- latitudes: np.array n_beams x n_gates array of geographic or AACGMv2 latitudes for range gate corners longitudes/mlts: np.array n_beams x n_gates array of geographic or AACGMv2 longitudes for range gate corners """ # Locate base PyDARN directory my_path = os.path.abspath(os.path.dirname(__file__)) base_path = os.path.join(my_path, '..') # Find files holding radar beam/gate locations beam_lats = base_path+'/radar_fov_files/' + \ str(stid).zfill(3)+'_lats.txt' beam_lons = base_path+'/radar_fov_files/' + \ str(stid).zfill(3)+'_lons.txt' # Read in geographic coordinates beam_corners_lats = np.loadtxt(beam_lats) beam_corners_lons = np.loadtxt(beam_lons) # AACGMv2 conversion if coords == 'aacgm': if not date: date = datetime.datetime.now() # Initialise arrays fan_shape = beam_corners_lons.shape beam_corners_aacgm_lons = np.zeros((fan_shape[0], fan_shape[1])) beam_corners_aacgm_lats = np.zeros((fan_shape[0], fan_shape[1])) for x in range(fan_shape[0]): for y in range(fan_shape[1]): # Conversion geomag = np.array( aacgmv2.get_aacgm_coord(beam_corners_lats[x, y], beam_corners_lons[x, y], 250, date)) beam_corners_aacgm_lats[x, y] = geomag[0] beam_corners_aacgm_lons[x, y] = geomag[1] # Return AACGMv2 latitudes and longitudes return beam_corners_aacgm_lats, beam_corners_aacgm_lons else: # Return geographic coordinates return beam_corners_lats, beam_corners_lons
def test_get_aacgm_coord_datetime_date(self): """Test single AACGMV2 calculation with date and datetime input""" self.in_args.extend([300.0, self.ddate, 'TRACE']) self.out = aacgmv2.get_aacgm_coord(*self.in_args) np.testing.assert_allclose(self.out, [58.2268,81.1613,0.1888], rtol=self.rtol)
import aacgmv2 import datetime as dt import numpy as np import pandas as pd np.set_printoptions(formatter={"float_kind": lambda x: "{:.2f}".format(x)}) dtime = dt.datetime(2013, 11, 3) x = pd.read_csv("csv/radar.csv") for rad, lat, lon in zip(x.rad, x.lat, x.lon): print("Rad(%s)" % rad, np.array(aacgmv2.get_aacgm_coord(lat, lon, 300, dtime))) y = pd.read_csv("csv/riometer_details.csv") for rio, lat, lon in zip(y.RIO, y.LAT, y.LON): print("Rio(%s)" % rio, (lat, np.round(np.mod((lon + 180), 360) - 180, 2)), np.array(aacgmv2.get_aacgm_coord(lat, lon, 300, dtime)))
def add_aacgm_coordinates(inst, glat_label='glat', glong_label='glong', alt_label='alt'): """ Uses AACGMV2 package to add AACGM coordinates to instrument object. The Altitude Adjusted Corrected Geomagnetic Coordinates library is used to calculate the latitude, longitude, and local time of the spacecraft with respect to the geomagnetic field. Example ------- # function added velow modifies the inst object upon every inst.load call inst.custom.add(add_quasi_dipole_coordinates, 'modify', glat_label='custom_label') Parameters ---------- inst : pysat.Instrument Designed with pysat_sgp4 in mind glat_label : string label used in inst to identify WGS84 geodetic latitude (degrees N) glong_label : string label used in inst to identify WGS84 geodetic longitude (degrees E) alt_label : string label used in inst to identify WGS84 geodetic altitude (km, height above surface) Returns ------- inst Input pysat.Instrument object modified to include quasi-dipole coordinates, 'aacgm_lat' for magnetic latitude, 'aacgm_long' for longitude, and 'aacgm_mlt' for magnetic local time. """ import aacgmv2 aalat = [] aalon = [] mlt = [] for lat, lon, alt, time in zip(inst[glat_label], inst[glong_label], inst[alt_label], inst.data.index): # aacgmv2 latitude and longitude from geodetic coords tlat, tlon, tmlt = aacgmv2.get_aacgm_coord(lat, lon, alt, time) aalat.append(tlat) aalon.append(tlon) mlt.append(tmlt) inst['aacgm_lat'] = aalat inst['aacgm_long'] = aalon inst['aacgm_mlt'] = mlt inst.meta['aacgm_lat'] = { 'units': 'degrees', 'long_name': 'AACGM latitude' } inst.meta['aacgm_long'] = { 'units': 'degrees', 'long_name': 'AACGM longitude' } inst.meta['aacgm_mlt'] = { 'units': 'hrs', 'long_name': 'AACGM Magnetic local time' } return
def occ_full_circle(station, year, month_range=None, day_range=None, gate_range=None, beam_range=None, time_units='mlt', plot_type='contour', local_testing=False): """ Produce a full circle stereographic plot in either ut or mlt (12 at the top). Can plot a simple echo count, ground scatter count, or average a fitACF parameter over the provided time range. Notes: - This program was originally written to be run on maxwell.usask.ca. This decision was made because occurrence investigations often require chewing large amounts of data. - Does not distinguish frequency - Only considers 45 km data. (a warning will be printed if other spatial resolution data is stripped from the dataset) - This program uses fitACF 3.0 data. To change this, modify the source code. :param station: str: The radar station to consider, a 3 character string (e.g. "rkn"). For a complete listing of available stations, please see https://superdarn.ca/radar-info :param year: int: The year to consider. :param month_range: (<int>, <int>) (optional): Inclusive. The months of the year to consider. If omitted (or None), then all days will be considered. :param day_range: (<int>, <int>) (optional): Inclusive. The days of the month to consider. If omitted (or None), then all days will be considered. :param gate_range: (<int>, <int>) (optional): Inclusive. The gate range to consider. If omitted (or None), then all the gates will be considered. Note that gates start at 0, so gates (0, 3) is 4 gates. :param beam_range: (<int>, <int>) (optional): Inclusive. The beam range to consider. If omitted (or None), then all beams will be considered. Note that beams start at 0, so beams (0, 3) is 4 beams. :param time_units: str: 'ut' for universal time or 'mlt' for magnetic local time: The time units to plot on the circle, 12 is always at the top. Default is 'mlt' :param plot_type: str (optional): The type of plot, either 'contour' or 'pixel', default is 'contour' :param local_testing: bool (optional): Set this to true if you are testing on your local machine. Program will then use local dummy data. :return: matplotlib.pyplot.figure: The figure. It can then be modified, added to, printed out, or saved in whichever file format is desired. """ time_units = check_time_units(time_units) year = check_year(year) all_radars_info = SuperDARNRadars() this_radars_info = all_radars_info.radars[pydarn.read_hdw_file( station).stid] # Grab radar info radar_id = this_radars_info.hardware_info.stid hemisphere = this_radars_info.hemisphere radar_lon = this_radars_info.hardware_info.geographic.lon radar_lat = this_radars_info.hardware_info.geographic.lat gate_range = check_gate_range(gate_range, this_radars_info.hardware_info) beam_range = check_beam_range(beam_range, this_radars_info.hardware_info) print("Retrieving data...") df = get_data_handler(station, year_range=(year, year), month_range=month_range, day_range=day_range, gate_range=gate_range, beam_range=beam_range, occ_data=True, local_testing=local_testing) df = only_keep_45km_res_data(df) # To compute mlt we need longitudes.. use the middle of the month as magnetic field estimate date_time_est, _ = build_datetime_epoch(year, 6, 15, 0) print("Computing MLTs for " + str(year) + " data...") cell_corners_aacgm_lats, cell_corners_aacgm_lons = radar_fov( stid=radar_id, coords='aacgm', date=date_time_est) df = add_mlt_to_df(cell_corners_aacgm_lons=cell_corners_aacgm_lons, cell_corners_aacgm_lats=cell_corners_aacgm_lats, df=df) # Get our raw x-data if time_units == "mlt": df['xdata'] = df['mlt'] else: print("Computing UTs for " + str(year) + " data...") ut_time = [] for i in range(len(df)): ut_time_here = df['datetime'].iat[i].hour + df['datetime'].iat[i].minute / 60 + \ df['datetime'].iat[i].second / 3600 if ut_time_here > 24: ut_time_here = ut_time_here - 24 elif ut_time_here < 0: ut_time_here = ut_time_here + 24 ut_time.append(ut_time_here) df['xdata'] = np.asarray(ut_time) print("Preparing the plot...") fig = plt.figure(figsize=(5, 5), dpi=300) lat_extreme = 40 * hemisphere.value # deg if hemisphere.value == 1: ax = fig.add_subplot(1, 1, 1, projection=ccrs.NorthPolarStereo()) elif hemisphere.value == -1: ax = fig.add_subplot(1, 1, 1, projection=ccrs.SouthPolarStereo()) else: raise Exception("hemisphere not recognized") ax.set_extent([-180, 180, hemisphere.value * 90, lat_extreme], crs=ccrs.PlateCarree()) # Compute a circle in axis coordinates which can be used as a boundary theta = np.linspace(0, 2 * np.pi, 100) center, radius = [0.5, 0.5], 0.5 vertices = np.vstack([np.sin(theta), np.cos(theta)]).T circle = mpath.Path(vertices * radius + center) ax.set_boundary(circle, transform=ax.transAxes) # Add gridlines and mlt labels text_offset_multiplier = 1.03 gl = ax.gridlines(draw_labels=True, linestyle='--', zorder=5) gl.xlocator = mticker.FixedLocator([-180, -135, -90, -45, 0, 45, 90, 135]) gl.xformatter = LONGITUDE_FORMATTER gl.yformatter = LATITUDE_FORMATTER # Print clock numbers ax.text(0, text_offset_multiplier * ax.get_ylim()[1], "12", ha='center', va='bottom') ax.text(0, text_offset_multiplier * ax.get_ylim()[0], "00", ha='center', va='top') ax.text(text_offset_multiplier * ax.get_xlim()[1], 0, "06", ha='left', va='center') ax.text(text_offset_multiplier * ax.get_xlim()[0], 0, "18", ha='right', va='center') # Print time units ax.text(text_offset_multiplier * ax.get_xlim()[0], text_offset_multiplier * ax.get_ylim()[1], time_units.upper(), ha='left', va='bottom') # Convert radar coordinates to aacgm and plot radar track radar_lat_aacgm, radar_lon_aacgm, radar_mlt = get_aacgm_coord( radar_lat, radar_lon, 0, date_time_est) radar_mlts = np.arange(0, 360, 1) radar_lats_aacgm = np.asarray([radar_lat_aacgm] * len(radar_mlts)) ax.plot(radar_mlts, radar_lats_aacgm, color='k', linewidth=0.5, linestyle="--", transform=ccrs.Geodetic(), label="Radar Path") # Right now xdata is in the range 0-24, we need to put it in the range 0-360 for circular plotting df['xdata'] = 15 * df['xdata'] print("Computing binned occ rates...") # Compute mlt edges deg_mlt_per_bin = 2 n_bins_mlt = int(360 / deg_mlt_per_bin) mlt_edges = np.linspace(0, 360, num=(n_bins_mlt + 1)) delta_mlt = mlt_edges[1] - mlt_edges[0] # Compute latitude edges n_bins_lat = 90 - abs(lat_extreme) # One bin per degree of latitude lat_edges = np.linspace(lat_extreme, 90, num=(n_bins_lat + 1)) delta_lat = lat_edges[1] - lat_edges[0] contour_data = np.empty(shape=(n_bins_mlt, n_bins_lat)) contour_data[:] = math.nan for mlt_idx, start_mlt in enumerate(mlt_edges): if start_mlt == mlt_edges[-1]: continue # The last edge is not a start end_mlt = start_mlt + delta_mlt df_mlt = df[(df['xdata'] >= start_mlt) & (df['xdata'] <= end_mlt)] for lat_idx, start_lat in enumerate(lat_edges): if start_lat == lat_edges[-1]: continue # The last edge is not a start end_lat = start_lat + delta_lat df_mlt_lat = df_mlt[(df_mlt['lat'] >= start_lat) & (df_mlt['lat'] <= end_lat)] try: contour_data[mlt_idx][lat_idx] = sum( df_mlt_lat['good_echo']) / len(df_mlt_lat) except ZeroDivisionError: # There are no point in this interval contour_data[mlt_idx, lat_idx] = math.nan except BaseException as e: print("MLT index: " + str(mlt_idx)) print("LAT index: " + str(lat_idx)) raise e contour_range = [[0, 360], [hemisphere.value * 37, hemisphere.value * 90]] # Compute bin centers bin_xwidth = (mlt_edges[1] - mlt_edges[0]) bin_ywidth = (lat_edges[1] - lat_edges[0]) bin_xcenters = mlt_edges[1:] - bin_xwidth / 2 bin_ycenters = lat_edges[1:] - bin_ywidth / 2 levels = 12 levels = np.linspace(start=0, stop=1, num=(levels + 1)) if plot_type == "contour": plot = ax.contourf(bin_xcenters, bin_ycenters, contour_data.transpose(), cmap='jet', levels=levels, transform=ccrs.PlateCarree()) elif plot_type == "pixel": plot = ax.imshow(np.flip(contour_data.transpose(), axis=0), aspect='auto', cmap="jet", transform=ccrs.PlateCarree()) else: raise Exception("plot_type not recognized") cbar = fig.colorbar(plot, ax=ax, shrink=0.75, orientation="horizontal", format='%.2f') cbar.ax.tick_params(labelsize=14, labelrotation=30) return df, fig
def test_get_aacgm_coord_maxalt_failure(self): """test get_aacgm_coord failure for an altitude too high for coeffs""" self.in_args.extend([2001, self.dtime, ""]) self.out = aacgmv2.get_aacgm_coord(*self.in_args) assert np.all(np.isnan(np.array(self.out)))