From 55b08ef24cdd22b83ebf74195dfb5bd400613802 Mon Sep 17 00:00:00 2001 From: Nico Vermaas <vermaas@astron.nl> Date: Mon, 25 Nov 2019 12:16:37 +0100 Subject: [PATCH] added fits_to_png functionality --- atdb_statistics/atdb_plot.py | 38 ++++++++++++++++++- atdb_stats.py | 30 ++++++++++++--- data/fits_to_png.args | 3 ++ data/sky.args | 2 +- data/time_used_science_pie_aug2019_prod.args | 4 ++ data/time_used_science_pie_aug2019_vm.args | 4 ++ data/time_used_system_pie_aug2019_prod.args | 4 ++ data/time_used_system_pie_jul2019_prod.args | 4 ++ data/times_today_prod.args | 4 +- dist/atdb_plot-1.0.0.tar.gz | Bin 3145 -> 4606 bytes 10 files changed, 83 insertions(+), 10 deletions(-) create mode 100644 data/fits_to_png.args create mode 100644 data/time_used_science_pie_aug2019_prod.args create mode 100644 data/time_used_science_pie_aug2019_vm.args create mode 100644 data/time_used_system_pie_aug2019_prod.args create mode 100644 data/time_used_system_pie_jul2019_prod.args diff --git a/atdb_statistics/atdb_plot.py b/atdb_statistics/atdb_plot.py index 63fd461..002681f 100644 --- a/atdb_statistics/atdb_plot.py +++ b/atdb_statistics/atdb_plot.py @@ -5,6 +5,7 @@ Description: atdb plot module """ +import os import datetime import plotly import plotly.graph_objs as go @@ -543,4 +544,39 @@ def do_time_used_science_pie_plot(title, subtitle, data): plt.grid(True,alpha=0.3) plt.legend(loc='upper right') - plt.show() \ No newline at end of file + plt.show() + + +def fits_to_png_apercal(input_file_name,output_file_name): + import matplotlib.pyplot as plt + from astropy.wcs import WCS + from astropy.io import fits + import matplotlib.colors as mc + output = output_file_name + fits_file = input_file_name + # open continuum image + fits_hdulist = fits.open(fits_file) + # get WCS header of cube + wcs = WCS(fits_hdulist[0].header) + # remove unnecessary axis + if wcs.naxis == 4: + wcs = wcs.dropaxis(3) + wcs = wcs.dropaxis(2) + img = fits_hdulist[0].data[0][0] + elif wcs.naxis == 3: + wcs = wcs.dropaxis(2) + img = fits_hdulist[0].data[0] + else: + img = fits_hdulist[0].data + # set up plot + ax = plt.subplot(projection=wcs) + # add image using sym-log scaling + fig = ax.imshow(img * 1.e3, norm=mc.SymLogNorm(1.e-9, vmin=0.03, vmax=200), origin='lower') + # add colorbar + cbar = plt.colorbar(fig) + cbar.set_label('Flux Density [mJy/beam]') + ax.coords[0].set_axislabel('Right Ascension') + ax.coords[1].set_axislabel('Declination') + ax.coords[0].set_major_formatter('hh:mm') + ax.set_title("{0:s}".format(os.path.basename(fits_file))) + plt.savefig(output, overwrite=True, bbox_inches='tight', dpi=300) \ No newline at end of file diff --git a/atdb_stats.py b/atdb_stats.py index 79d8cd6..a5439f9 100644 --- a/atdb_stats.py +++ b/atdb_stats.py @@ -310,8 +310,9 @@ def do_times(args): if result['ingest_speed'] is not None and result['ingest_speed'] > 0: datapoint = {} datapoint['taskid'] = result['taskID'] - nofrag,frag = result['timestamp_ingesting'].split('.') - timestamp = datetime.datetime.strptime(nofrag, '%Y-%m-%dT%H:%M:%S') + # nofrag,frag = result['timestamp_ingesting'].split('.') + # timestamp = datetime.datetime.strptime(nofrag, '%Y-%m-%dT%H:%M:%S') + timestamp = datetime.datetime.strptime(result['timestamp_ingesting'], '%Y-%m-%dT%H:%M:%SZ') datapoint['timestamp'] = timestamp datapoint['type'] = 'ingesting' datapoint['duration'] = result['ingest_duration'] @@ -325,8 +326,9 @@ def do_times(args): if result['timestamp_ingest_error'] is not None: datapoint = {} datapoint['taskid'] = result['taskID'] - nofrag,frag = result['timestamp_ingest_error'].split('.') - timestamp = datetime.datetime.strptime(nofrag, '%Y-%m-%dT%H:%M:%S') + # nofrag,frag = result['timestamp_ingest_error'].split('.') + # timestamp = datetime.datetime.strptime(nofrag, '%Y-%m-%dT%H:%M:%S') + timestamp = datetime.datetime.strptime(result['timestamp_ingest_error'], '%Y-%m-%dT%H:%M:%SZ') datapoint['timestamp'] = timestamp datapoint['type'] = 'ingest_error' datapoint['speed_bps'] = prev_ingest_speed @@ -515,6 +517,12 @@ def main(): parser.add_argument("--interval", default="day", help="Shows bars per interval. Possible options: minute, hour, day, month") + parser.add_argument("--input_filename","-i", + default="", + help="input filename") + parser.add_argument("--output_filename","-o", + default="", + help="output filename") # plot parameters parser.add_argument("--title", default="Title", @@ -534,6 +542,12 @@ def main(): parser.add_argument("--colormap", default="viridis", help="see: https://matplotlib.org/examples/color/colormaps_reference.html") + + # commands + parser.add_argument("--fits2png", + default=False, + help="convert FITS to PNG.", + action="store_true") # All parameters in a file parser.add_argument('--argfile', nargs='?', @@ -544,15 +558,16 @@ def main(): help="Show current version of this program.", action="store_true") + args = get_arguments(parser) # -------------------------------------------------------------------------------------------------------- if (args.version): - print('--- atdb_stats.py - version 1.0.0 - 9 jun 2019 ---') + print('--- atdb_stats.py - version 1.1.0 - 25 nov 2019 ---') print('Copyright (C) 2019 - Nico Vermaas - ASTRON. This program comes with ABSOLUTELY NO WARRANTY;') return - print('--- atdb_stats.py - version 1.0.0 - 9 jun 2019 ---') + print('--- atdb_stats.py - version 1.1.0 - 25 nov 2019 ---') print('Copyright (C) 2019 - Nico Vermaas - ASTRON. This program comes with ABSOLUTELY NO WARRANTY;') if args.starttime != None: starttime = datetime.datetime.strptime(args.starttime, TIME_FORMAT) @@ -604,6 +619,9 @@ def main(): elif presentation=="time_used": do_time_used(args) + elif args.fits2png: + atdb_plot.fits_to_png_apercal(args.input_filename,args.output_filename) + if args.remote_post_command != None: execute_remote_command(args.atdb_host, args.remote_post_command) diff --git a/data/fits_to_png.args b/data/fits_to_png.args new file mode 100644 index 0000000..8a56bf5 --- /dev/null +++ b/data/fits_to_png.args @@ -0,0 +1,3 @@ +--fits2png +--input_file=D:\my_happili\image_mf_04.fits +--output_file=D:\my_happili\image_mf_04.png diff --git a/data/sky.args b/data/sky.args index f5437f2..54d4042 100644 --- a/data/sky.args +++ b/data/sky.args @@ -7,4 +7,4 @@ --plot_type=scatter --colormap=viridis --starttime=2018-01-01 00:00 ---endtime=2019-06-17 00:00 +--endtime=2019-11-12 00:00 diff --git a/data/time_used_science_pie_aug2019_prod.args b/data/time_used_science_pie_aug2019_prod.args new file mode 100644 index 0000000..8dae508 --- /dev/null +++ b/data/time_used_science_pie_aug2019_prod.args @@ -0,0 +1,4 @@ +--presentation=time_used +--atdb_host=http://192.168.22.22/atdb +--title=APERTIF Science Pie - Aug 2019 +--query=from=2019-08-01T00:01:00Z&to=2019-09-01T00:00:00Z&report_type=time_used_science_pie \ No newline at end of file diff --git a/data/time_used_science_pie_aug2019_vm.args b/data/time_used_science_pie_aug2019_vm.args new file mode 100644 index 0000000..8dae508 --- /dev/null +++ b/data/time_used_science_pie_aug2019_vm.args @@ -0,0 +1,4 @@ +--presentation=time_used +--atdb_host=http://192.168.22.22/atdb +--title=APERTIF Science Pie - Aug 2019 +--query=from=2019-08-01T00:01:00Z&to=2019-09-01T00:00:00Z&report_type=time_used_science_pie \ No newline at end of file diff --git a/data/time_used_system_pie_aug2019_prod.args b/data/time_used_system_pie_aug2019_prod.args new file mode 100644 index 0000000..230a8f1 --- /dev/null +++ b/data/time_used_system_pie_aug2019_prod.args @@ -0,0 +1,4 @@ +--presentation=time_used +--atdb_host=http://atdb.astron.nl/atdb +--title=APERTIF System Pie - Aug 2019 +--query=from=2019-08-01T00:01:00Z&to=2019-09-01T00:00:00Z&report_type=time_used_system_pie \ No newline at end of file diff --git a/data/time_used_system_pie_jul2019_prod.args b/data/time_used_system_pie_jul2019_prod.args new file mode 100644 index 0000000..473a224 --- /dev/null +++ b/data/time_used_system_pie_jul2019_prod.args @@ -0,0 +1,4 @@ +--presentation=time_used +--atdb_host=http://atdb.astron.nl/atdb +--title=APERTIF System Pie - Aug 2019 +--query=from=2019-08-01T00:01:00Z&to=2019-08-31T00:08:00Z&report_type=time_used_system_pie \ No newline at end of file diff --git a/data/times_today_prod.args b/data/times_today_prod.args index 7d2dc72..4bc4a3a 100644 --- a/data/times_today_prod.args +++ b/data/times_today_prod.args @@ -1,6 +1,6 @@ --presentation=times --atdb_host=http://atdb.astron.nl/atdb --title=I/O speeds from ATDB ---subtitle=18 - 23 july 2019 +--subtitle=29/30 aug 2019 --y_axis_title=I/O Speed in Gbps ---query=starttime__gt=2019-07-18T00:00:00Z&starttime__lt=2019-07-23T00:00:00Z +--query=starttime__gt=2019-08-29T00:00:00Z&starttime__lt=2019-09-02T00:00:00Z diff --git a/dist/atdb_plot-1.0.0.tar.gz b/dist/atdb_plot-1.0.0.tar.gz index f295e205a10fd66827184cf2702917da251893c5..2ee7381f7c37a53cb77e46153e9d6b7361e6bbca 100644 GIT binary patch literal 4606 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 literal 3145 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 Qq(9haR>W^-s9<0K0O;i^M*si- -- GitLab