Browse Source

Full transition to Material Design (except doc)

tags/v3.1
Benito Marcote 6 months ago
parent
commit
f8d2b9f48d
5 changed files with 756 additions and 141 deletions
  1. +169
    -137
      app.py
  2. +559
    -0
      assets/style.css
  3. +2
    -0
      assets/style.css.old
  4. +1
    -1
      data/stations_catalog.inp
  5. +25
    -3
      vlbiplanobs/graphical_elements.py

+ 169
- 137
app.py View File

@@ -314,7 +314,7 @@ def skip_intro_choices(clicks_wizard, clicks_expert, clicks_pickband, clicks_pic
clicks_continuum, clicks_line,
a_wavelength, a_array, time_selection, starttime, starthour, obs_duration):
if clicks_expert is not None:
return main_page(None)
return main_page()
elif clicks_wizard is not None:
return initial_page('band')
elif clicks_pickband is not None:
@@ -325,10 +325,9 @@ def skip_intro_choices(clicks_wizard, clicks_expert, clicks_pickband, clicks_pic
#TODO: if dates are provided, all date/time/dur must be provided
return initial_page('mode')
elif clicks_continuum is not None:
return compute_observation(n_clicks, band, starttime, starthour, duration, source, onsourcetime,
datarate, subbands, channels, pols, inttime, guest_time, selected_tab, *ants)
return initial_page('final')
elif clicks_line is not None:
return dash.no_update
return initial_page('final')
else:
return dash.no_update

@@ -339,14 +338,14 @@ def initial_page(choice_card):
return [
html.Div(className='row justify-content-center',
children=html.Div(className='col-sm-6 justify-content-center',
children=[html.Div(className='justify-content-center',
children=[#html.H3("Welcome!"),
html.P(["The EVN Observation Planner allows you to plan observations with the ",
html.A(href="https://www.evlbi.org", children="European VLBI Network"),
" (EVN) and other Very Long Baseline Interferometry (VLBI) networks. "
"The EVN Observation Planner helps you to determine when your source "
"can be observed by the different antennas, and provides the expected "
"outcome of these observations, like the expected sensitivity or resolution."]),
children=[html.Div(className='justify-content-center',
children=[#html.H3("Welcome!"),
html.P(["The EVN Observation Planner allows you to plan observations with the ",
html.A(href="https://www.evlbi.org", children="European VLBI Network"),
" (EVN) and other Very Long Baseline Interferometry (VLBI) networks. "
"The EVN Observation Planner helps you to determine when your source "
"can be observed by the different antennas, and provides the expected "
"outcome of these observations, like the expected sensitivity or resolution."]),
html.Br(),
*[
html.Div(hidden=False if choice_card == 'choice' else True,
@@ -359,6 +358,8 @@ def initial_page(choice_card):
children=ge.initial_window_pick_time()),
html.Div(hidden=False if choice_card == 'mode' else True,
children=ge.initial_window_pick_mode(app)),
html.Div(hidden=False if choice_card == 'final' else True,
children=ge.initial_window_final()),
],
], style={'text:align': 'justify !important'})
])
@@ -366,10 +367,32 @@ def initial_page(choice_card):



def main_page(a_wavelength):
a_band = tuple(fs.bands)[a_wavelength] if a_wavelength is not None else None
def final_guess_window():
return [
# First row containing all buttons/options, list of telescopes, and button with text output
html.Div(),
html.Div(className='row justify-content-center', children=[
html.H3('You are know ready'),
html.P(["Press compute to produce the summary for your observation. "
"You would then see different tabs with the information. "
"You will also be able to change the setup and re-compute it."]),
html.Br(),
]),
html.Span(style={'height': '2rem'}),
html.Div(className='row justify-content-center',
children=html.Button('Compute', id='antenna-selection-button',
className='btn btn-primary btn-lg')),
html.Div(className='col-9 text-center justify-content-center', children=[
dcc.Loading(id="loading", children=[html.Div(id="loading-output")],
type="dot"),
dcc.Loading(id="loading2", children=[html.Div(id="loading-output2")],
type="dot")
])
]



def main_page(results_visible=False):
return [# First row containing all buttons/options, list of telescopes, and button with text output
dcc.ConfirmDialog(id='global-error', message=''),
# Elements in second column (checkboxes with all stations)
html.Div(className='container-fluid', children=[
@@ -380,49 +403,75 @@ def main_page(a_wavelength):
html.Div('', style={'height': '70px'}),
dcc.Loading(id="loading2", children=[html.Div(id="loading-output2")],
type="dot"),
html.Div(id='div-antenna-selection-button2', children=[]),
html.Label('Your observing Band'),
*ge.tooltip(idname='popover-band',
html.Button('Compute observation', id='antenna-selection-button',
className='btn btn-primary btn-lg',
style={'width': '100%', 'margin-bottom': '1rem'}),
]),
html.Br(),
html.Div(className='form-group', children=[
html.H6(['Your observing Band',
*ge.tooltip(idname='popover-band',
message="This will update the "
"antenna list showing the ones that can observe "
"at that given frequency."),
dcc.Dropdown(id='band', persistence=True, value=a_band,
"at that given frequency.")
]),
dcc.Dropdown(id='band', persistence=True, value=None,
options=[{'label': fs.bands[b], 'value': b} for b \
# in fs.bands], value='18cm'),
in fs.bands], placeholder='Select observing band...')

in fs.bands], placeholder='Select observing band...'),
]),
html.Div(className='input-group-prepend', children=[
html.Br(),
html.Div(className='form-group', children=[
html.H6(['Real-time correlation?',
*ge.tooltip(idname='popover-eevn',
message="Only available for the EVN: real-time correlation mode."
"The data are transferred and correlated in real-time, but "
"not all telescopes are capable for this and the bandwidth "
"may be limited. Observations during the e-EVN epochs.")
]),
dbc.Checklist(id='e-EVN', className='checkbox', persistence=True,
options=[{'label': ' e-EVN (real-time) mode',
options=[{'label': ' e-EVN mode',
'value': 'e-EVN'}], value=[]),
*ge.tooltip(idname='popover-eevn',
message="Only available for the EVN: real-time correlation mode.")
]),
html.Br(),
html.Div(className='form-group', children=[
html.Label('Source (name or coordinates)'),
*ge.tooltip(idname='popover-target',
html.H6(['Source (name or coordinates)',
*ge.tooltip(idname='popover-target',
message="Source name or coordinates. " \
"You may see an error if the given name is not properly resolved. "
"J2000 coordinates are assumed in both forms: 00:00:00 00:00:00 or " \
"00h00m00s 00d00m00s."),
# dcc.Input(id='source', value='12:29:06.7 +02:03:08.6', type='text',
"00h00m00s 00d00m00s.")
]),
dcc.Input(id='source', value=None, type='text',
className='form-control', placeholder="hh:mm:ss dd:mm:ss",
persistence=True),
html.Small(id='error_source', style={'color': '#999999'},
className='form-text'),
]),
dbc.Tabs(children=[
dbc.Tab(label='Pick Epoch', id='tab-pick-epoch', tabClassName='tab-for-card', children=[
html.Div(className='form-group', children=[
dbc.Card(className='card-no-left-border', children=dbc.CardBody([
html.Br(),
html.Div(className='form-group', children=[
html.H6('Epoch for observation'),
dbc.FormGroup([
dbc.RadioItems(options=[{"label": "I don't have a preferred epoch", "value": False},
{"label": "I know the observing epoch", "value": True}],
value=False, id="initial-timeselection", inline=True, persistence=True),
], inline=True),
html.Div(children=[
html.Div(id='initial-timeselection-div-guess', className='row justify-content-center',
children=[
html.Small("Choose the first option to find out when your source "
"may be visible (by >3 telescopes).", style={'color': '#999999'}),
html.Small("Note that this option may not provide the best (expected) "
"results in case of combining different networks very far apart "
"(e.g. LBA and EVN).", style={'color': '#999999'})
]),
html.Div(id='initial-timeselection-div-epoch', children=[
html.Label('Start of observation (UTC)'),
*ge.tooltip(idname='popover-startime', message="Select the date and "
"time of the start of the observation (Universal, UTC, "
"time). You will also see the day of the year (DOY) in "
"brackets once the date is selected."),
html.Br(),
dcc.DatePickerSingle(id='starttime', date=None, min_date_allowed=dt(1900, 1, 1),
max_date_allowed=dt(2100, 1, 1),
display_format='DD-MM-YYYY (DDD)',
@@ -431,54 +480,41 @@ def main_page(a_wavelength):
initial_visible_month=dt.today(),
persistence=True,
className='form-picker'),
dcc.Dropdown(id='starthour', placeholder="Start time", value=None,
dcc.Dropdown(id='starthour', placeholder="Start time (UTC)", value=None,
options=[{'label': f"{hm//60:02n}:{hm % 60:02n}", \
'value': f"{hm//60:02n}:{hm % 60:02n}"} \
for hm in range(0, 24*60, 15)],
persistence=True, className='form-hour'),
html.Small(id='error_starttime', style={'color': 'red'},
className='form-text text-muted'),
html.Label('Duration of the observation (in hours)'),
html.Div(className='form-group', children=[
html.Label('Duration of the observation (hours)'),
*ge.tooltip(idname='popover-duration', message="Select the total duration of the "
"observation (provided in hours)."),
dcc.Input(id='duration', value=None, type='number', className='form-control',
placeholder="In hours", persistence=True, inputMode='numeric'),
html.Small(id='error_duration', style={'color': 'red'}, className='form-text text-muted')
])]))
]),
placeholder="Duration in hours", persistence=True, inputMode='numeric'),
html.Small(id='error_duration', style={'color': 'red'},
className='form-text text-muted')
])
])
]),
dbc.Tab(label='Guess Times', id='tab-guess-times', tabClassName='tab-for-card', children=[
html.Div(className='form-group', children=[
dbc.Card(className='card-no-left-border', children=dbc.CardBody([
html.P("Choose this option if you just want to find out when your source "
"will be visible. It will pick the time range when more than 3 antennas "
"can observe."),
dbc.Checklist(id='guest-times', className='checkbox', persistence=True,
options=[{'label': " I don't have preferred times",
'value': 'guest-times'}], value=[]),
html.Small("Note that this option may not provide the wished results if "
"different networks far apart (e.g. LBA + EVN) are selected.",
style={'color': '#999999'})
])
)])
])
]),
html.Br(),
html.Div(className='form-group', children=[
html.Label(id='onsourcetime-label',
children='% of on-target time'),
*ge.tooltip(idname='popover-ontarget',
html.H6(['% of on-target time',
*ge.tooltip(idname='popover-ontarget',
message="Assumes that you will only spend this amount of the total " \
"observing time on the given target source. It affects the " \
"expected sensitivity."),
]),
dcc.Slider(id='onsourcetime', min=20, max=100, step=5, value=70,
marks= {i: str(i) for i in range(20, 101, 10)},
persistence=True),
html.Label(id='onsourcetime-label', style={'color': '#999999'},
children='70% of the observation.'),
]),
html.H4("Advanced setup"),
html.Br(),
html.Div(className='form-group', children=[
html.Label('Datarate per station'),
*ge.tooltip(idname='popover-datarate',
html.H6(['Datarate per station',
*ge.tooltip(idname='popover-datarate',
message=["Expected datarate for each station, assuming all " \
"of them run at the same rate.",
html.Ul([
@@ -487,46 +523,55 @@ def main_page(a_wavelength):
html.Li("The VLBA can now observe up to 4 Gbps."),
html.Li("The LBA typically runs at 512 Mbps but can reach up to 1 Gbps."),
html.Li("Check the documentation from other networks to be " \
"sure about their capabilities.")])]),
"sure about their capabilities.")])])
]),
dcc.Dropdown(id='datarate',
placeholder="Select the data rate...",
options=[{'label': fs.data_rates[dr], 'value': dr} \
for dr in fs.data_rates], value=2048, persistence=True),
html.Label(id='bandwidth-label', style={'color': '#999999'}, children='')
]),
html.Div(className='form-group', children=[
html.Label('Number of subbands'),
*ge.tooltip(idname='popover-subbands',
html.H6(['Number of subbands',
*ge.tooltip(idname='popover-subbands',
message="Number of subbands to split the total observed bandwidth "
" during correlation (IFs in AIPS)."),
" during correlation (IFs in AIPS).")
]),
dcc.Dropdown(id='subbands', placeholder="Select no. subbands...",
options=[{'label': fs.subbands[sb], 'value': sb} \
for sb in fs.subbands], value=8, persistence=True),
]),
html.Br(),
html.Div(className='form-group', children=[
html.Label('Number of spectral channels'),
*ge.tooltip(idname='popover-channels',
html.H6(['Number of spectral channels',
*ge.tooltip(idname='popover-channels',
message="How many channels per subband will be produced "
"during correlation."),
dcc.Dropdown(id='channels', placeholder="Select no. channels...",
options=[{'label': fs.channels[ch],
'value': ch} \
for ch in fs.channels], value=32, persistence=True),
"during correlation.")
]),
dcc.Dropdown(id='channels', placeholder="Select no. channels...",
options=[{'label': fs.channels[ch],
'value': ch} \
for ch in fs.channels], value=32, persistence=True),
]),
html.Br(),
html.Div(className='form-group', children=[
html.Label('Number of polarizations'),
*ge.tooltip(idname='popover-pols',
html.H6(['Number of polarizations',
*ge.tooltip(idname='popover-pols',
message="Number of polarizations to correlate. Note that VLBI uses circular " \
"polarizations. Full polarization implies the four stokes: RR, LL, RL, LR; " \
"while dual polarization implies RR and LL only."),
"while dual polarization implies RR and LL only.")
]),
dcc.Dropdown(id='pols', placeholder="Select polarizations...",
options=[{'label': fs.polarizations[p], 'value': p} \
for p in fs.polarizations], value=4, persistence=True),
]),
html.Br(),
html.Div(className='form-group', children=[
html.Label('Integration time (s)'),
*ge.tooltip(idname='popover-inttime',
html.H6(['Integration time',
*ge.tooltip(idname='popover-inttime',
message="Integration time to compute each visibility. Note that for continuum " \
"observations values of 1-2 seconds are typical."),
"observations values of 1-2 seconds are typical.")
]),
dcc.Dropdown(id='inttime', placeholder="Select integration time...",
options=[{'label': fs.inttimes[it], 'value': it} \
for it in fs.inttimes], value=2, persistence=True),
@@ -540,35 +585,29 @@ def main_page(a_wavelength):
html.Div(className='row justify-content-center', children=[
html.Div(className='col-9', children=[
html.Div(id='first-advise', className='col-sm-9', children=[
html.P(["Here you can set up your observation.", html.Br(),
"Please select which network (or networks) you want to use in your "
"observations, or select a customized array of antennas. "
"On the left panel you can set the basic information from your observations: "
"times of the observations and target source to observe. ", html.Br(),
"Optionally, you can customize the configuration and correlation parameters "
"under 'advance setup'. Otherwise, default values based on your selection "
"will be used.", html.Br(),
"Once you are ready, press the big red ", html.B("'compute observation'"),
" button. You will get a detailed "
"summary of the planned observation and expected outcomes in the different "
html.H4("Customize your observation"),
html.P(["Select which VLBI network(s) you want to use in your "
"observation, or select an ad-hoc array of antennas. ", html.Br(),
"Set the basic information from your observations: "
"observing band, target source, epoch, and observing setup. ", html.Br(),
"Finally, press the blue ", html.B("'compute observation'"),
" button. ", html.Br(),
"You will get a detailed "
"summary of the planned observation and expected outcome in the different "
"tabs."]),
html.P(["Note that only antennas that can observe at the selected band "
"will be clickable."])
html.P(html.Em(["Note that only antennas that can observe at the selected band "
"will be clickable."]))
], style={'margin-top': '2rem', 'margin-bottom': '2rem'}),
html.Div(className='col-9 form-group row align-items-end', children=[
html.Div(className='col-md-6', children=[
html.Label('Select default VLBI Network(s)',
style={'color': '#a01d26'}),
html.Label('Select default VLBI Network(s)'),
# style={'color': '#a01d26'}),
*ge.tooltip(idname='popover-network', message="Automatically selects "
"the default participating antennas for the selected VLBI network(s)."),
dcc.Dropdown(id='array', options=[{'label': n, 'value': n} \
for n in default_arrays if n != 'e-EVN'], value=[],
persistence=True, multi=True),
]),
html.Div(id='div-antenna-selection-button', className='col-sm-3', children=[
html.Button('Compute Observation', id='antenna-selection-button',
className='btn btn-primary btn-lg'),
])
]),
html.Div(className='col-9 text-center justify-content-center', children=[
dcc.Loading(id="loading", children=[html.Div(id="loading-output")],
@@ -590,9 +629,8 @@ def main_page(a_wavelength):
disabled=not s.has_band(selected_band)),
dbc.Label(s.name, html_for=f"check_{s.codename}",
id=f"_input_{s.codename}",
className='custom-control-label form-check-label')
], check=True, inline=True, className="form-check-input "
"custom-checkbox custom-control custom-control-inline")
className='custom-control-label')
], check=True, inline=True, className="custom-checkbox custom-control custom-control-inline")
for s in all_antennas if s.network == an_array
])
]) for an_array in sorted_networks
@@ -690,43 +728,36 @@ def main_page(a_wavelength):



@app.callback([Output('tab-pick-epoch', 'label'),
Output('tab-guess-times', 'label')],
[Input('guest-times', 'value')])
def update_tab_time_labels(guest_time):
"""Updates the labels in the tabs where the user can either pick a specific observing
time or let the app to guest the correct times.
It will add a green tick or red cross to the option that is currently selected.
@app.callback(Output('onsourcetime-label', 'children'),
[Input('onsourcetime', 'value'),
Input('duration', 'value'),
Input('initial-timeselection', 'value')])
def update_onsourcetime_label(onsourcetime, total_duration, defined_epoch):
"""Keeps the on-source time label updated with the value selected by the user.
"""
if guest_time:
return "Pick Epoch ", "Guest Times ✔️"
else:
return "Pick Epoch ✔️", "Guest Times ❌"
if (total_duration is not None) and defined_epoch:
print(total_duration)
return f"{onsourcetime}% of the total time ({ge.optimal_units(total_duration*u.h*onsourcetime/100, [u.h, u.min, u.s]):.03n})."

return f"{onsourcetime}% of the total observation."



@app.callback([Output('div-antenna-selection-button', 'children'),
Output('div-antenna-selection-button2', 'children')],
[Input('tabs', 'value')])
def move_compute_button(selected_tab):
"""Depending on which tab is selected, it will show the button to compute the observation
in one place or another, so it is always visible and clickable.
@app.callback(Output('bandwidth-label', 'children'),
[Input('datarate', 'value'),
Input('pols', 'value')])
def update_bandwidth_label(datarate, npols):
"""Updates the total bandwidth label as a function of the selected datarate and number of
polarizations. Returns a string with the value and units.
"""
if selected_tab == 'tab-setup' or selected_tab == 'tab-doc':
return html.Button('Compute Observation', id='antenna-selection-button',
className='btn btn-primary btn-lg'), html.Div('', style={'height': '2.3rem'})
else:
return html.Div('', style={'height': '2.3rem'}), html.Button('Compute again',
id='antenna-selection-button', className='btn btn-primary btn-lg',
style={'width': '100%', 'margin-bottom': '1rem'}),
if (None not in (datarate, npols)) and (datarate != -1):
# Either 1 or 2 pols per station:
temp = npols % 3 + npols // 3
return [f"The total bandwidth is {ge.optimal_units(datarate*u.MHz/(temp*2*2), [u.GHz, u.MHz, u.kHz] )}.",
html.Br(), html.Br()]

return ''

@app.callback(Output('onsourcetime-label', 'children'),
[Input('onsourcetime', 'value')])
def update_onsourcetime_label(onsourcetime):
"""Keeps the on-source time label updated with the value selected by the user.
"""
return f"% of on-target time ({onsourcetime}%)"


@app.callback([Output(f"check_{s.codename}", 'checked') for s in all_antennas] + \
@@ -828,11 +859,11 @@ def get_source(source_coord):
State('channels', 'value'),
State('pols', 'value'),
State('inttime', 'value'),
State('guest-times', 'value'),
State('initial-timeselection', 'value'),
State('tabs', 'value')] + \
[State(f"check_{s.codename}", 'checked') for s in all_antennas])
def compute_observation(n_clicks, band, starttime, starthour, duration, source, onsourcetime,
datarate, subbands, channels, pols, inttime, guest_time, selected_tab, *ants):
datarate, subbands, channels, pols, inttime, epoch_selected, selected_tab, *ants):
"""Computes all products to be shown concerning the set observation.
"""
# To decide where to put the output message
@@ -841,7 +872,7 @@ def compute_observation(n_clicks, band, starttime, starthour, duration, source,
return '', '', dash.no_update, dash.no_update, dash.no_update, \
dash.no_update, dash.no_update

if not guest_time:
if epoch_selected:
# All options must be completed
if None in (band, starttime, starthour, duration, source, datarate, subbands, channels, pols, inttime) \
or source == "":
@@ -889,7 +920,7 @@ def compute_observation(n_clicks, band, starttime, starthour, duration, source,
"First, set correctly an observation in the previous tab.", '']
return *[temp if out_center else temp[::-1]][0], \
dash.no_update, dash.no_update, dash.no_update, dash.no_update, dash.no_update
if guest_time:
if not epoch_selected:
try:
if starttime is not None:
utc_times, _ = observation.Observation.guest_times_for_source(target_source,
@@ -955,7 +986,7 @@ def compute_observation(n_clicks, band, starttime, starthour, duration, source,

# TODO: parallelize all these fig functions
if out_center:
if guest_time:
if not epoch_selected:
return [html.Br(), dbc.Alert("You can check now the results in the different tabs", color='info', \
dismissable=True),
*alert_message("Note that you have selected the 'guest time' option. "
@@ -1104,3 +1135,4 @@ if __name__ == '__main__':
app.run_server(host='0.0.0.0', debug=True)




+ 559
- 0
assets/style.css View File

@@ -119,3 +119,562 @@ input[type=time] {
}


.card-no-left-border {
border-left: 0px;
border: 1px solid #CCCCCC !important;
}

.tab-for-card > .active {
border: 1px solid #CCCCCC !important
}

.card.card-antenna {
background-color: white !important;
min-width: 12rem;
max-width: 12rem;
height: 20rem;
font-size: 0.8rem;
border: 1px solid #a01d26 !important;
/* text-align: left; */
/* text-color: #a01d26; */
/* border: none; */
/* outline: none; */
/* border: 1px solid #CCCCCC; */
margin: 8px;
}

.card.card-antenna .card-img {
object-fit: cover;
width: 100%;
height: 40%;
overflow: hidden;
border-bottom-left-radius: 0;
border-bottom-right-radius: 0;
}

.card.card-antenna .card-title {
font-size: 1rem;
margin-bottom: 0.3rem;
}

.card.card-antenna .card-title2 {
font-size: 0.9rem;
}

.card.card-antenna .card-subtitle {
color: #AAAAAA;
font-size: 0.8rem;
margin-bottom: 0.5rem;
}

.card.card-antenna .card-body {
padding: 0.9rem;
}

.card.card-antenna .card-body p {
margin-top: 0px;
margin-bottom: 0px;
}

.card.card-antenna .card-text {
margin-bottom: 0.5rem;
position: absolute;
bottom: 0.5rem;
}

.popover-link {
color: #a01d26;
}

.baseline-table {
align: center;
margin-bottom: -0.2rem;
}

.baseline-tr {
width: 100%;
}

.baseline-td-img {
vertical-align: middle;
}

.baseline-td-hr {
vertical-align: bottom;
}

.hr-baseline {
border: 2px solid #a01d26 !important;
border-radius: 4rem;
}

.col.col-baseline {
margin-left: 0;
margin-right: 0;
padding-left: 0;
padding-right: 0;
column-width: 31px !important;
}

.img-baseline {
margin-left: 0.4rem;
margin-right: 0.4rem;
padding-left: 0;
padding-right: 0;
vertical-align: baseline;
}


.fig-on-card {
width: 30rem;
}

.bandpass {
border: 3px solid #a01d26;
border-bottom: 0px solid #a01d26;
border-top-left-radius: 20%;
border-top-right-radius: 20%;
display: inline-block;
margin-right: -2px;
margin-left: -1px;
margin-bottom: 0;
margin-top: 1rem;
}

.tooltip-card-inner {
border-color: 0 !important;
border: 0;
padding: 0;
margin: 0;
background-color: transparent !important;
}

.arrow.tooltip-card-arrow {
color: transparent;
border: 0;
background-color: transparent !important;
}

/* */
/* .card { */
/* min-width: 21rem; */
/* max-width: 21rem; */
/* border: 1px solid #CCCCCC; */
/* border-left: 5px solid #a01d26; */
/* } */
/* */
/* .card-title { */
/* color: #a01d26; */
/* } */
/* */
/* input[type=checkbox] { */
/* -webkit-appearance: none; */
/* -moz-appearance: none; */
/* appearance: none; */
/* /* transform: scale(1.5); */ */
/* } */
/* */
/* input[type=checkbox] { */
/* width: 17px; */
/* height: 17px; */
/* line-height: 17px; */
/* margin-right: 0px; */
/* margin-bottom: 20px; */
/* cursor: pointer; */
/* font-size: 17px; */
/* visibility: hidden; */
/* padding: 0; */
/* } */
/* */
/* input[type=checkbox]:after { */
/* content: " "; */
/* background-color: #fff; */
/* display: inline; */
/* color: #00BFF0; */
/* width: 17px; */
/* height: 17px; */
/* visibility: visible; */
/* border: 1px solid #00BFF0; */
/* padding-left: 5px; */
/* border-radius: 5px; */
/* margin-bottom: 0px; */
/* } */
/* */
/* input[type=checkbox]:not(:disabled):after { */
/* content: " "; */
/* font-weight: bold; */
/* color: #00BFF0; */
/* border: 1px solid #00BFF0; */
/* } */
/* */
/* input[type=checkbox]:not(:disabled):not(checked):hover:after { */
/* content: ""; */
/* color: gray; */
/* background-color: rgb(232, 222, 222); */
/* } */
/* */
/* input[type=checkbox]:checked:after { */
/* content: "\2714"; */
/* font-family: "FontAwesome"; */
/* /* content: "\f00c"; */ */
/* font-weight: bold; */
/* color: green; */
/* font-size: 1.1rem; */
/* -moz-font-size: 1.1rem; */
/* /* line-height: 0px; */ */
/* border: none; */
/* background-color: none; */
/* margin-left: -0.1rem; */
/* } */
/* */
/* input[type=checkbox]:not(:disabled):checked:hover:after { */
/* /* content: " "; */ */
/* /* color: gray; */ */
/* background-color: rgb(232, 222, 222); */
/* content: "\2714"; */
/* /* font-weight: bold; */ */
/* color: green; */
/* /* font-size: 1.1rem; */ */
/* } */
/* */
/* */
/* input[type=checkbox]:disabled:after { */
/* /* content: "X"; */ */
/* font-family: "FontAwesome"; */
/* content: "\2715"; */
/* border: none; */
/* /* color: #a01d26; */ */
/* color: #F0959B; */
/* background-color: none; */
/* margin-left: -0.1rem; */
/* } */
/* */
/* /* #band { */ */
/* /* background-color: #F0959B; */ */
/* /* } */ */
/* */

.antcheck label {
padding-left: 0;
padding-right: 2rem;
}

.custom-checkbox {
width: 12rem;
padding-left: 0;
padding-right: 2rem;
}

.custom-control-input {
position: absolute;
display: inline-block;
margin-top: .3rem;
/* margin-left:-1.25rem; */
}

.custom-control-label {
margin-left: 1.25rem;
}

/* input[type=checkbox]:disabled ~ .antcheck2 label { */
.custom-control-input:disabled ~ .custom-control-label {
text-decoration: line-through;
color: #6c757d;
}

.custom-control-input:disabled {
background-color:#6c757d;
}

.custom-control-input:checked ~ .custom-control-label {
color: green;
}

.custom-checkbox .custom-control-input:checked~.custom-control-label::before {
border: none;
background-color: green;
}



/* .form-check-input { */
/* position: absolute; */
/* margin-top: .3rem; */
/* margin-left:-1.25rem; */
/* display: inline-block; */
/* } */
/* */
/* .form-check-input:disabled ~ .form-check-label { */
/* color:#6c757d; */
/* } */
/* */
/* .form-check-label { */
/* margin-bottom: 0; */
/* } */
/* */
/* .form-check-inline { */
/* display: inline-flex; */
/* align-items: center; */
/* padding-left: 0; */
/* margin-right: 0.75rem; */
/* width: 9rem; */
/* } */
/* */
/* */
/* .form-check-inline .form-check-input { */
/* position: static; */
/* margin-top: 0; */
/* margin-right: .3125rem; */
/* margin-left: 0; */
/* } */
/* */
/* .form-inline .form-check { */
/* width:100%; */
/* } */

/* */
/* @media (min-width: 576px) { */
/* .form-inline label { */
/* display: flex; */
/* align-items: center; */
/* justify-content: center; */
/* margin-bottom: 0; */
/* margin-left: 2rem; */
/* } */
/* */
/* .form-inline .form-group { */
/* display: flex; */
/* flex: 0 0 auto; */
/* flex-flow: row wrap; */
/* align-items: center; */
/* margin-bottom:0; */
/* } */
/* */
/* .form-inline .form-control { */
/* display: inline-block; */
/* width: auto; */
/* vertical-align:middle; */
/* } */
/* */
/* .form-inline .form-control-plaintext { */
/* display:inline-block; */
/* } */
/* */
/* .form-inline .input-group, .form-inline .custom-select { */
/* width:auto; */
/* } */
/* */
/* .form-inline .form-check { */
/* display: flex; */
/* align-items: center; */
/* justify-content: center; */
/* width: auto; */
/* padding-left:0; */
/* } */
/* */
/* .form-inline .form-check-input { */
/* position: relative; */
/* flex-shrink: 0; */
/* margin-top: 0; */
/* margin-right: .25rem; */
/* margin-left:0; */
/* } */
/* */
/* .form-inline .custom-control { */
/* align-items: center; */
/* justify-content:center; */
/* } */
/* */
/* .form-inline .custom-control-label { */
/* margin-bottom:0; */
/* } */
/* } */
/* */
/* .was-validated .form-check-input:invalid ~ .form-check-label, .form-check-input.is-invalid ~ .form-check-label { */
/* color:#06648C; */
/* } */
/* */
/* .was-validated .form-check-input:invalid ~ .invalid-feedback, .was-validated .form-check-input:invalid ~ .invalid-tooltip, .form-check-input.is-invalid ~ .invalid-feedback, .form-check-input.is-invalid ~ .invalid-tooltip { */
/* display:block */
/* } */
/* */
/* .was-validated .form-check-input:valid ~ .form-check-label, .form-check-input.is-valid ~ .form-check-label { */
/* color:#015668 */
/* } */
/* */
/* .was-validated .form-check-input:valid ~ .valid-feedback, .was-validated .form-check-input:valid ~ .valid-tooltip, .form-check-input.is-valid ~ .valid-feedback, .form-check-input.is-valid ~ .valid-tooltip { */
/* display:block */
/* } */




.card.card-antenna {
background-color: white !important;
min-width: 12rem;
max-width: 12rem;
height: 20rem;
font-size: 0.8rem;
border: 1px solid #a01d26 !important;
text-align: left;
/* text-color: #a01d26; */
/* border: none; */
/* outline: none; */
/* border: 1px solid #CCCCCC; */
margin: 8px;
}

.card.card-antenna .card-img {
object-fit: cover;
width: 100%;
height: 40%;
overflow: hidden;
border-bottom-left-radius: 0;
border-bottom-right-radius: 0;
}

.card.card-antenna .card-title {
font-size: 1rem;
margin-bottom: 0.3rem;
color: #a01d26;
}

.card.card-antenna .card-title2 {
font-size: 0.8rem;
color: #555555;
}

.card.card-antenna .card-subtitle {
color: #999999;
font-size: 0.8rem;
margin-bottom: 0.5rem;
}

.card.card-antenna .card-body {
padding: 0.9rem;
}

.card.card-antenna .card-body p {
margin-top: 0px;
margin-bottom: 0px;
color: #999999;
}

.card.card-antenna .card-text {
margin-bottom: 0.5rem;
margin-right: 0.2rem;
position: absolute;
bottom: 0.5rem;
color: #999999;
}


.card-summary {
background-color: white !important;
/* color: #a01d26; */
/* cursor: pointer; */
/* padding: 18px; */
width: 100% !important;
/* text-align: left; */
/* text-color: #a01d26; */
/* border: none; */
/* outline: none; */
min-width: 100%;
max-width: 100%;
/* border: 1px solid #CCCCCC; */
border-left: 5px solid #a01d26;
margin: 40px;
min-width: 21rem;
max-width: 21rem;
border: 1px solid #CCCCCC;
border-left: 5px solid #a01d26;
border-radius: .25rem;
}

.card-summary .card-title {
color: #a01d26;
transition: 0.4s;
font-weight: normal;
}


.card-summary .card-header {
background-color: white !important;
/* color: white; */
/* cursor: pointer; */
/* padding: 18px; */
width: 100%;
text-align: left;
/* color: #a01d26; */
/* outline: none; */
transition: 0.4s;
margin-bottom: 1;
border-bottom: 0px !important;
}

.card-summary .card-header h2 .btn {
text-color: #a01d26;
font-weight: 500;
line-height:1.2;
font-size: 1.75rem;
}

/* Add a background color to the button if it is clicked on (add the .active class with JS), and when you move the mouse over it (hover) */
/* .active, .accordion:hover { */
/* background-color: #ccc; */
/* } */

/* Style the accordion panel. Note: hidden by default */
.accordion-collapse.collapse {
background-color: white;
border-bottom: 1px solid #CCCCCC;
/* display: none; */
/* overflow: hidden; */
}



img[alt=equation] {
/* width: 50%; */
height: 2.5rem;
display: block;
margin-left: auto;
margin-right: auto;
margin-top: 1rem;
margin-bottom: 1rem;
}

img[alt=equation2] {
/* width: 50%; */
height: 1.5rem;
display: block;
margin-left: auto;
margin-right: auto;
margin-top: 1rem;
margin-bottom: 1rem;
}

.card-deck{
display:flex;
flex-direction:column;
}

.card-deck .card{
margin-bottom:15px;
}

@media (min-width: 576px){
.card-deck{
flex-flow:row wrap;
margin-right:-15px;
margin-left:-15px;
}
.card-deck .card{
display:flex;
flex:1 0 0%;
flex-direction:column;
margin-right:15px;
margin-bottom:0;
margin-left:15px;
}
}

+ 2
- 0
assets/style.css.old View File

@@ -3743,6 +3743,7 @@ img[alt=equation2] {
/* border: none; */
/* outline: none; */
/* border: 1px solid #CCCCCC; */
text-align: left;
margin: 8px;
}

@@ -3762,6 +3763,7 @@ img[alt=equation2] {

.card.card-antenna .card-title2 {
font-size: 0.9rem;
text-align: left;
}

.card.card-antenna .card-subtitle {


+ 1
- 1
data/stations_catalog.inp View File

@@ -591,7 +591,7 @@ station = Kunming
code = Km
network = EVN
possible_networks = EVN
country = United Kingdom
country = China
diameter = 40 m
#img =
#link =


+ 25
- 3
vlbiplanobs/graphical_elements.py View File

@@ -57,7 +57,7 @@ def create_sensitivity_card(title, message):
ps = [html.P(className='card-text', children=message)]

# return [html.Div(className='card', style={'min-width': '15rem', 'max-width': '25rem'}, children=[
return [html.Div(className='card m-3', children=[
return [html.Div(className='card-summary m-3', children=[
html.Div(className='card-body', children=[
html.H5(className='card-title', children=title)] + ps)])
]
@@ -208,8 +208,8 @@ def summary_card_times(app, obs):
temp_msg += [[*img_fits, html.Br(),
f"With a time integration of {optimal_units(obs.inttime, [u.s,u.ms,u.us]):.2n} the "
f"expected FITS file size is "
f"{optimal_units(obs.datasize(), [u.TB, u.GB, u.MB, u.kB]):.3n} "
f"(divided in {n_files} 2-GB files)."]]
f"{optimal_units(obs.datasize(), [u.TB, u.GB, u.MB, u.kB]):.3n}. "]]
# f"(divided in {n_files} 2-GB files)."]]
return create_sensitivity_card('Observing Time', temp_msg)


@@ -572,3 +572,25 @@ def initial_window_pick_mode(app):
]


def initial_window_final():
return [
html.Div(className='row justify-content-center', children=[
html.H3('You are know ready'),
html.P(["Press compute to produce the summary for your observation. "
"You would then see different tabs with the information. "
"You will also be able to change the setup and re-compute it."]),
html.Br(),
]),
html.Span(style={'height': '2rem'}),
html.Div(className='row justify-content-center',
children=html.Button('Compute', id='antenna-selection-button',
className='btn btn-primary btn-lg')),
html.Div(className='col-9 text-center justify-content-center', children=[
dcc.Loading(id="loading", children=[html.Div(id="loading-output")],
type="dot"),
dcc.Loading(id="loading2", children=[html.Div(id="loading-output2")],
type="dot")
])
]



Loading…
Cancel
Save