import datetime
import io
from hydralit import HydraHeadApp
import astropy.units as u
import pandas as pd
import streamlit as st
from astropy.coordinates import SkyCoord
from sunpy.coordinates import frames
from apps.extras.backmapping import *
import hydralit_components as hc
class SolarMach(HydraHeadApp):
def __init__(self, title = '', **kwargs):
self.__dict__.update(kwargs)
self.title = title
def run(self):
st.title('Solar-MACH')
st.subheader('Source for this great app is from the Streamlit gallery [Solar-MACH](https://github.com/jgieseler/Solar-MACH). An example of how easy it is to convert an existing application and use within a Hydralit multi-page application, see the secret saurce [here] (https://github.com/TangleSpace/hydralit).')
st.markdown('
',unsafe_allow_html=True)
st.markdown('## Multi-spacecraft longitudinal configuration plotter')
# provide date and time
with st.sidebar.container():
d = st.sidebar.date_input("Select date", datetime.date.today()-datetime.timedelta(days = 2))
t = st.sidebar.time_input('Select time', datetime.time(1, 30))
date = datetime.datetime.combine(d, t).strftime("%Y-%m-%d %H:%M:%S")
# plotting settings
with st.sidebar.container():
st.sidebar.subheader('Plot options:')
plot_spirals = st.sidebar.checkbox('Parker spiral for each body', value=True)
plot_sun_body_line = st.sidebar.checkbox('Straight line from Sun to body', value=True)
show_earth_centered_coord = st.sidebar.checkbox('Add Earth-aligned coord. system', value=False)
transparent = st.sidebar.checkbox('Transparent background', value=False)
plot_reference = st.sidebar.checkbox('Plot reference (e.g. flare)', value=True)
with st.sidebar.expander("Reference coordinates (e.g. flare)", expanded=plot_reference):
reference_sys = st.radio('Coordinate system:', ['Carrington', 'Stonyhurst'], index=0)
if reference_sys == 'Carrington':
reference_long = st.slider('Longitude:', 0, 360, 20)
reference_lat = st.slider('Latitude:', -90, 90, 0)
if reference_sys == 'Stonyhurst':
reference_long = st.slider('Longitude:', -180, 180, 20)
reference_lat = st.slider('Latitude:', -90, 90, 0)
# convert Stonyhurst coordinates to Carrington for further use:
coord = SkyCoord(reference_long*u.deg, reference_lat*u.deg, frame=frames.HeliographicStonyhurst, obstime=date)
coord = coord.transform_to(frames.HeliographicCarrington(observer='Sun'))
reference_long = coord.lon.value
reference_lat = coord.lat.value
import math
reference_vsw = int(float(st.text_input('Solar wind speed for reference', 400)))
if plot_reference is False:
reference_long = None
reference_lat = None
st.sidebar.subheader('Choose bodies/spacecraft and measured solar wind speeds')
with st.sidebar.container():
full_body_list = \
st.sidebar.text_area('Bodies/spacecraft (scroll down for example list)',
'STEREO A, Earth, BepiColombo, PSP, Solar Orbiter, Mars',
height=50)
vsw_list = \
st.sidebar.text_area('Solar wind speed per body/SC (mind the order!)', '400, 400, 400, 400, 400, 400',
height=50)
body_list = full_body_list.split(',')
vsw_list = vsw_list.split(',')
body_list = [body_list[i].strip() for i in range(len(body_list))]
wrong_vsw = False
try:
vsw_list = [int(vsw_list[i].strip()) for i in range(len(vsw_list))]
except ValueError:
wrong_vsw = True
all_bodies = print_body_list()
# ugly workaround to not show the index in the table: replace them with empty strings
all_bodies.reset_index(inplace=True)
all_bodies.index = [""] * len(all_bodies)
st.sidebar.table(all_bodies['Key'])
st.sidebar.markdown('[Complete list of available bodies](https://ssd.jpl.nasa.gov/horizons.cgi?s_target=1#top)')
if wrong_vsw:
st.error('ERROR: There is something wrong in the solar wind speed list! Maybe some missing or wrong comma?')
st.stop()
if len(body_list) == len(vsw_list):
# initialize the bodies
c = HeliosphericConstellation(date, body_list, vsw_list, reference_long,
reference_lat)
# make the longitudinal constellation plot
plot_file = 'Solar-MACH_'+datetime.datetime.combine(d, t).strftime("%Y-%m-%d_%H-%M-%S")+'.png'
c.plot(
plot_spirals=plot_spirals, # plot Parker spirals for each body
plot_sun_body_line=plot_sun_body_line, # plot straight line between Sun and body
show_earth_centered_coord=show_earth_centered_coord, # display Earth-aligned coordinate system
reference_vsw=reference_vsw, # define solar wind speed at reference
transparent = transparent,
# outfile=plot_file # output file (optional)
)
# download plot
filename = 'Solar-MACH_'+datetime.datetime.combine(d, t).strftime("%Y-%m-%d_%H-%M-%S")
plot2 = io.BytesIO()
plt.savefig(plot2, format='png', bbox_inches="tight")
# plot3 = base64.b64encode(plot2.getvalue()).decode("utf-8").replace("\n", "")
# st.markdown(f'Download figure as .png file', unsafe_allow_html=True)
download_button_str = self.download_button(plot2.getvalue(), filename+'.png', f'Download figure as .png file', pickle_it=False)
#st.markdown(download_button_str, unsafe_allow_html=True)
# display coordinates table
df = c.coord_table
df.index = df['Spacecraft/Body']
df = df.drop(columns=['Spacecraft/Body'])
df = df.round(0)
df = df.rename(columns=
{"Spacecraft/Body": "Spacecraft / body",
"Carrington Longitude (°)": "Carrington longitude",
"Latitude (°)": "Carrington latitude",
"Heliocentric Distance (AU)": "Heliocent. distance",
"Longitudinal separation to Earth's longitude": "Longitud. separation to Earth longitude",
"Latitudinal separation to Earth's latitude": "Latitud. separation to Earth latitude",
"Vsw": "Solar wind speed",
"Magnetic footpoint longitude (Carrington)": "Magnetic footpoint Carrington longitude",
"Longitudinal separation between body and reference_long": "Longitud. separation bw. body & reference",
"Longitudinal separation between body's mangetic footpoint and reference_long": "Longitud. separation bw. body's magnetic footpoint & reference",
"Latitudinal separation between body and reference_lat": "Latitudinal separation bw. body & reference"})
st.table(df.T)
# download coordinates
# filename = 'Solar-MACH_'+datetime.datetime.combine(d, t).strftime("%Y-%m-%d_%H-%M-%S")
# csv = c.coord_table.to_csv().encode()
# b64 = base64.b64encode(csv).decode()
# st.markdown(f'Download table as .csv file', unsafe_allow_html=True)
download_button_str = self.download_button(c.coord_table, filename+'.csv', f'Download table as .csv file', pickle_it=False)
#st.markdown(download_button_str, unsafe_allow_html=True)
else:
st.error(f"ERROR: Number of elements in the bodies/spacecraft list \
({len(body_list)}) and solar wind speed list ({len(vsw_list)}) \
don't match! Please verify that for each body there is a solar \
wind speed provided!")
# footer
st.markdown("""---""")
st.markdown('The *Solar MAgnetic Connection Haus* (Solar-MACH) tool is a \
multi-spacecraft longitudinal configuration plotter. It was \
originally developed at the University of Kiel, Germany, and further \
discussed within the [ESA Heliophysics Archives USer (HAUS)]\
(https://www.cosmos.esa.int/web/esdc/archives-user-groups/heliophysics) \
group. It is now opened to everyone ([original code]\
(https://github.com/esdc-esac-esa-int/Solar-MACH)).')
st.markdown('[Forked and modified](https://github.com/jgieseler/Solar-MACH) by \
[J. Gieseler](https://jgieseler.github.io) (University of Turku, Finland). \
[**Get in contact**](mailto:jan.gieseler@utu.fi?subject=Solar-MACH).')
col1, col2 = st.columns((5,1))
col1.markdown("The development of the online tool has received funding from the \
European Union's Horizon 2020 research and innovation programme \
under grant agreement No 101004159 (SERPENTINE).")
col2.markdown('[](https://serpentine-h2020.eu)', unsafe_allow_html=True)
st.markdown('Powered by: \
[](https://matplotlib.org) \
[](https://streamlit.io) \
[](https://sunpy.org)', \
unsafe_allow_html=True)