diff --git a/README.md b/README.md index 538fa3a28..5d2895139 100644 --- a/README.md +++ b/README.md @@ -4,6 +4,9 @@ Portal for scheduling observations of NEOs (and other Solar System objects) usin ## History +### 3.10.1 +* Fix orbit plots for cometary elements. + ### 3.10.0 * Add support for DAMIT and advanced LC plotting features. diff --git a/neoexchange/astrometrics/ephem_subs.py b/neoexchange/astrometrics/ephem_subs.py index 9f857c68c..75a6ab7c9 100644 --- a/neoexchange/astrometrics/ephem_subs.py +++ b/neoexchange/astrometrics/ephem_subs.py @@ -2247,15 +2247,24 @@ def convert_to_ecliptic(equatorial_coords, coord_date): def orbital_pos_from_true_anomaly(nu, body_elements): - """Give Cartesian coordinates for a given true anomaly in radians for s given set of orbital elements""" - a = body_elements['meandist'] + """Give Cartesian coordinates for a given true anomaly in radians for a given set of orbital elements""" + a = body_elements.get('meandist', 0) e = body_elements['eccentricity'] i = radians(body_elements['orbinc']) om = radians(body_elements['longascnode']) w = radians(body_elements['argofperih']) + q = body_elements.get('perihdist', 0) + + # Calculate radial distance + if not a: # Comet + e = 1 + if e >= 1: # shorten extent of nu to prevent wayward infinities and beyond. + nu *= acos(-1/e) / pi - 0.0001 + r = q * (1 + e) / (1 + e * cos(nu)) + else: + r = a * (1 - e ** 2) / (1 + e * cos(nu)) # Calculate radial distance and heliocentric, ecliptic Cartesian Coordinates based on orbital elements - r = a * (1 - e ** 2) / (1 + e * cos(nu)) x_h_ecl = r * (cos(om) * cos(w + nu) - sin(om) * sin(w + nu) * cos(i)) y_h_ecl = r * (sin(om) * cos(w + nu) + cos(om) * sin(w + nu) * cos(i)) z_h_ecl = r * (sin(w + nu) * sin(i)) diff --git a/neoexchange/core/plots.py b/neoexchange/core/plots.py index 7e9e2bffc..56204c2a9 100644 --- a/neoexchange/core/plots.py +++ b/neoexchange/core/plots.py @@ -730,8 +730,14 @@ def lc_plot(lc_list, meta_list, lc_model_dict={}, period=1, pscan_list=[], shape plot_period.y_range = DataRange1d(names=['period']) plot_period.x_range = DataRange1d(names=['period'], bounds=(0, None)) plot_orbit = figure(plot_width=900, plot_height=900, x_axis_location=None, y_axis_location=None) - plot_orbit.y_range = Range1d(min(-1.1, -1.1 * body.meandist), max(1.1, 1.1 * body.meandist)) - plot_orbit.x_range = Range1d(min(-1.1, -1.1 * body.meandist), max(1.1, 1.1 * body.meandist)) + if body.meandist: + orbit_range = body.meandist + elif body.perihdist: + orbit_range = body.perihdist + else: + orbit_range = 1 + plot_orbit.y_range = Range1d(min(-1.1, -1.1 * orbit_range), max(1.1, 1.1 * orbit_range)) + plot_orbit.x_range = Range1d(min(-1.1, -1.1 * orbit_range), max(1.1, 1.1 * orbit_range)) plot_orbit.grid.grid_line_color = None plot_shape = figure(plot_width=600, plot_height=600, x_axis_location=None, y_axis_location=None) plot_shape.grid.grid_line_color = None @@ -1193,7 +1199,7 @@ def get_orbit_position(meta_list, lc_list, body): def get_full_orbit(body): body_elements = model_to_dict(body) - full_orbit = [orbital_pos_from_true_anomaly(nu, body_elements) for nu in np.arange(0, 7, .01)] + full_orbit = [orbital_pos_from_true_anomaly(nu, body_elements) for nu in np.arange(-pi, pi, .01)] full_orbit_x = [pos[0] for pos in full_orbit] full_orbit_y = [pos[1] for pos in full_orbit] full_orbit_z = [pos[2] for pos in full_orbit] @@ -1201,7 +1207,7 @@ def get_full_orbit(body): planetary_elements = get_planetary_elements() for planet in planetary_elements: - p_orb = [orbital_pos_from_true_anomaly(nu, planetary_elements[planet]) for nu in np.arange(0, 7, .01)] + p_orb = [orbital_pos_from_true_anomaly(nu, planetary_elements[planet]) for nu in np.arange(-pi, pi, .01)] position_data[f'{planet}_x'] = [pos[0] for pos in p_orb] position_data[f'{planet}_y'] = [pos[1] for pos in p_orb] position_data[f'{planet}_z'] = [pos[2] for pos in p_orb] diff --git a/neoexchange/neox/settings.py b/neoexchange/neox/settings.py index 37efb99fe..3942d0c31 100644 --- a/neoexchange/neox/settings.py +++ b/neoexchange/neox/settings.py @@ -7,7 +7,7 @@ import rollbar -VERSION = '3.10.0' +VERSION = '3.10.1' CURRENT_PATH = os.path.dirname(os.path.realpath(__file__)) diff --git a/neoexchange/neox/tests/test_plot_lc.py b/neoexchange/neox/tests/test_plot_lc.py index 506150b7b..1ff5968dd 100644 --- a/neoexchange/neox/tests/test_plot_lc.py +++ b/neoexchange/neox/tests/test_plot_lc.py @@ -41,9 +41,9 @@ def setUp(self): super(LighCurvePlotTest, self).setUp() settings.DATA_ROOT = self.test_dir settings.MEDIA_ROOT = self.test_dir - lcname = '433_738215_ALCDEF.txt' + self.lcname = '433_738215_ALCDEF.txt' lcpath = os.path.abspath(os.path.join('photometrics', 'tests')) - save_to_default(os.path.join(lcpath, lcname), lcpath) + save_to_default(os.path.join(lcpath, self.lcname), lcpath) params = { 'name' : '433', 'abs_mag' : 21.0, @@ -72,6 +72,33 @@ def setUp(self): self.body2.save_physical_parameters({'parameter_type': 'P', 'value': 5.27}) + params = {'name': 'C/2017 K2', + 'abs_mag': 7.0, + 'slope': 2.0, + 'epochofel': '2021-07-05 00:00:00', + 'epochofperih': '2022-12-19 05:50:00', + 'argofperih': 236.19001, + 'longascnode': 88.244, + 'orbinc': 87.55297, + 'eccentricity': 1.0005709, + 'perihdist': 1.7974686, + 'source_type': 'C', + 'elements_type': 'MPC_COMET', + 'active': True, + 'origin': 'O', + 'ingest': '2015-05-11 17:20:00', + 'score': 90, + 'discovery_date': '2015-05-10 12:00:00', + 'update_time': '2015-05-18 05:00:00', + 'num_obs': 17, + 'arc_length': 3.123456789, + 'not_seen': 0.423456789, + 'updated': True, + 'meandist': None, + 'meananom': None, + } + self.comet, created = Body.objects.get_or_create(pk=3, **params) + self.username = 'bart' self.password = 'simpson' self.email = 'bart@simpson.org' @@ -83,8 +110,6 @@ def setUp(self): update_proposal_permissions(self.bart, [{'code': self.neo_proposal.code}]) - save_dataproduct(self.body2, lcname, DataProduct.ALCDEF_TXT) - @patch('neox.auth_backend.lco_authenticate', mock_lco_authenticate) def login(self): self.browser.get('%s%s' % (self.live_server_url, '/accounts/login/')) @@ -108,6 +133,7 @@ def test_no_lightcurve(self): @patch('neox.auth_backend.lco_authenticate', mock_lco_authenticate) def test_can_view_lightcurve(self): # test opening up a ALCDEF file associated with a body + save_dataproduct(self.body2, self.lcname, DataProduct.ALCDEF_TXT) self.login() lc_url = reverse('lc_plot', args=[self.body2.id]) self.browser.get(self.live_server_url + lc_url) @@ -135,3 +161,21 @@ def test_can_view_lightcurve(self): # test opening up a ALCDEF file associated alcdef_text = self.browser.find_element_by_xpath("/html/body/pre").text self.assertIn('OBJECTNUMBER=433', alcdef_text) return + + @patch('neox.auth_backend.lco_authenticate', mock_lco_authenticate) + def test_can_view_comet_lightcurve(self): # test opening up a ALCDEF file associated with a body + save_dataproduct(self.comet, self.lcname, DataProduct.ALCDEF_TXT) + self.login() + lc_url = reverse('lc_plot', args=[self.comet.id]) + self.browser.get(self.live_server_url + lc_url) + + self.assertIn('Lightcurve for object: C/2017 K2', self.browser.title) + + canvas = self.browser.find_element_by_xpath( + "/html/body[@class='page']/div[@id='page-wrapper']/div[@id='page']/div[@id='main']/div[@name='lc_plot']/div[@class='bk']/div[@class='bk'][2]/div[@class='bk'][1]/div[@class='bk']/div[@class='bk bk-canvas-events']") + phase_tab = self.browser.find_element_by_xpath( + "/html/body[@class='page']/div[@id='page-wrapper']/div[@id='page']/div[@id='main']/div[@name='lc_plot']/div[@class='bk']/div[@class='bk bk-tabs-header bk-above']/div[@class='bk bk-headers-wrapper']/div[@class='bk bk-headers']/div[@class='bk bk-tab']") + phase_tab.click() + period_box = self.browser.find_element_by_xpath( + "/html/body[@class='page']/div[@id='page-wrapper']/div[@id='page']/div[@id='main']/div[@name='lc_plot']/div[@class='bk']/div[@class='bk'][2]/div[@class='bk'][2]/div[@class='bk'][2]/div[@class='bk']/div[@class='bk'][1]/div[@class='bk'][1]/div[@class='bk']/div[@class='bk bk-input-group']/div[@class='bk bk-spin-wrapper']/input[@class='bk bk-input']") + return