From baa68c5746d2e6b8b0521fc64ac3b1d6f9470fef Mon Sep 17 00:00:00 2001 From: Rutger van Haasteren <vhaasteren@gmail.com> Date: Tue, 11 Jul 2023 09:52:09 +0200 Subject: [PATCH] Updated colors a bit. Need more proper solution though --- pylk/pylk/colormodes.py | 419 ++++++++++++++++++++++++++++++++++++++++ pylk/pylk/constants.py | 16 +- pylk/pylk/plk.py | 15 +- 3 files changed, 439 insertions(+), 11 deletions(-) create mode 100644 pylk/pylk/colormodes.py diff --git a/pylk/pylk/colormodes.py b/pylk/pylk/colormodes.py new file mode 100644 index 0000000..1e64bed --- /dev/null +++ b/pylk/pylk/colormodes.py @@ -0,0 +1,419 @@ +""" Color modes for graphed pintk TOAs. """ +import numpy as np +import matplotlib +import matplotlib.colors + + +# subset of other colors to allow users to distinguish between them +named_colors = [ + "xkcd:red", + "xkcd:green", + "xkcd:cyan", + "xkcd:blue", + "xkcd:burnt orange", + "xkcd:brown", + "xkcd:indigo", + "xkcd:purple", + "xkcd:dark blue", + "xkcd:light green", + "xkcd:dark green", + "xkcd:light blue", + "xkcd:dark red", + "xkcd:magenta", + "xkcd:black", + "xkcd:grey", + "xkcd:light grey", + "xkcd:yellow", + "xkcd:orange", +] + + +class ColorMode: + """Base Class for color modes.""" + + def __init__(self, application): + self.application = application # PLKWidget for pintk + + def displayInfo(self): + raise NotImplementedError + + def plotColorMode(self): + raise NotImplementedError + + +class DefaultMode(ColorMode): + """ + A class to manage the Default color mode, where TOAs are colored + blue as a default and red if jumped. + """ + + def __init__(self, application): + super().__init__(application) + self.mode_name = "default" + + def displayInfo(self): + print( + '"Default" mode selected\n' + #+ " Blue = default color\n" + #+ " Orange = selected TOAs\n" + #+ " Red = jumped TOAs\n" + + " Cyan = default color\n" + + " Orange = selected TOAs\n" + + " Magenta = jumped TOAs\n" + ) + + def plotColorMode(self): + """ + Plots application's residuals in proper color scheme. + """ + if self.application.yerrs is None: + self.application.plkAxes.scatter( + self.application.xvals[~self.application.selected], + self.application.yvals[~self.application.selected], + marker=".", + color="xkcd:cyan", + ) + self.application.plkAxes.scatter( + self.application.xvals[self.application.jumped], + self.application.yvals[self.application.jumped], + marker=".", + color="xkcd:magenta", + ) + self.application.plkAxes.scatter( + self.application.xvals[self.application.selected], + self.application.yvals[self.application.selected], + marker=".", + color="xkcd:burnt orange", + ) + else: + self.application.plotErrorbar(~self.application.selected, color="xkcd:cyan") + self.application.plotErrorbar(self.application.jumped, color="xkcd:burnt orange") + self.application.plotErrorbar(self.application.selected, color="xkcd:magenta") + + +class FreqMode(ColorMode): + """ + A class to manage the Frequency color mode, where TOAs are colored + according to their frequency. + """ + + def __init__(self, application): + super().__init__(application) + self.mode_name = "freq" + + def displayInfo(self): + print( + '"Frequency" mode selected\n' + + " Dark Red < 300 MHz\n" + + " Red = 300-400 MHz\n" + + " Orange = 400-500 MHz\n" + + " Yellow = 500-700 MHz\n" + + " Green = 700-1000 MHz\n" + + " Blue = 1000-1800 MHz\n" + + " Indigo = 1800-3000 MHz\n" + + " Black = 3000-8000 MHz\n" + + " Grey > 8000 MHz\n" + + " Brown is for selected TOAs\n" + ) + + def plotColorMode(self): + """ + Plots application's residuals in proper color scheme. + """ + + colorGroups = [ + "xkcd:dark red", # dark red + "xkcd:red", # red + "xkcd:orange", # orange + "xkcd:yellow", # yellow + "xkcd:green", # green + "xkcd:blue", # blue + "xkcd:indigo", # indigo + "xkcd:black", # black + "xkcd:grey", # grey + ] + highfreqs = [300.0, 400.0, 500.0, 700.0, 1000.0, 1800.0, 3000.0, 8000.0] + + freqGroups = [] + for ii, highfreq in enumerate(highfreqs): + if ii == 0: + freqGroups.append( + self.application.psr.all_toas.get_freqs().value < highfreq + ) + else: + freqGroups.append( + (self.application.psr.all_toas.get_freqs().value < highfreq) + & ( + self.application.psr.all_toas.get_freqs().value + >= highfreqs[ii - 1] + ) + ) + freqGroups.append( + self.application.psr.all_toas.get_freqs().value >= highfreqs[-1] + ) + + for index in range(len(freqGroups)): + if self.application.yerrs is None: + self.application.plkAxes.scatter( + self.application.xvals[freqGroups[index]], + self.application.yvals[freqGroups[index]], + marker=".", + color=colorGroups[index], + ) + else: + self.application.plotErrorbar( + freqGroups[index], color=colorGroups[index] + ) + # The following is for selected TOAs + if self.application.yerrs is None: + self.application.plkAxes.scatter( + self.application.xvals[self.application.selected], + self.application.yvals[self.application.selected], + marker=".", + color="#362511", # brown + ) + else: + self.application.plotErrorbar(self.application.selected, color="#362511") + + +class NameMode(ColorMode): + """ + A class to manage the Frequency color mode, where TOAs are colored + according to their names in the TOA lines. + """ + + def __init__(self, application): + super().__init__(application) + self.mode_name = "name" + + def displayInfo(self): + print('"Name" mode selected\n' + " Orange = selected TOAs\n") + + def plotColorMode(self): + """ + Plots application's residuals in proper color scheme. + """ + + all_names = np.array( + [f["name"] for f in self.application.psr.all_toas.get_flags()] + ) + single_names = list(set(all_names)) + N = len(single_names) + cmap = matplotlib.cm.get_cmap("brg") + colorGroups = [matplotlib.colors.rgb2hex(cmap(v)) for v in np.linspace(0, 1, N)] + colorGroups += ["orange"] + + freqGroups = [] + index = 0 + for name in single_names: + index += 1 + freqGroups.append(all_names == name) + index += 1 + + for index in range(N): + if self.application.yerrs is None: + self.application.plkAxes.scatter( + self.application.xvals[freqGroups[index]], + self.application.yvals[freqGroups[index]], + marker=".", + color=colorGroups[index], + ) + else: + self.application.plotErrorbar( + freqGroups[index], color=colorGroups[index] + ) + + if self.application.yerrs is None: + self.application.plkAxes.scatter( + self.application.xvals[self.application.selected], + self.application.yvals[self.application.selected], + marker=".", + color=colorGroups[N], + ) + else: + self.application.plotErrorbar( + self.application.selected, color=colorGroups[N] + ) + + +class ObsMode(ColorMode): + """ + A class to manage the Observatory color mode, where TOAs are colored + according to their observatory. + """ + + def __init__(self, application): + super().__init__(application) + self.mode_name = "obs" + + def get_obs_mapping(self): + "This maps the obs names in the TOAs to our local subset" + tmp_obs = self.application.psr.all_toas.observatories + mapping = {} + for oo in tmp_obs: + if "stl" in oo: + mapping[oo] = "space" + elif oo.startswith("gb"): + mapping[oo] = "gb" + elif oo.startswith("jb"): + mapping[oo] = "jodrell" + elif "ncy" in oo: + mapping[oo] = "nancay" + else: + mapping[oo] = oo if oo in self.obs_colors else "other" + return mapping + + obs_colors = { + "parkes": "xkcd:red", + "gb": "xkcd:green", # this is any green bank obs + "jodrell": "xkcd:cyan", + "arecibo": "xkcd:blue", + "chime": "xkcd:burnt orange", + "gmrt": "xkcd:brown", + "vla": "xkcd:indigo", + "effelsberg": "xkcd:purple", + "fast": "xkcd:dark blue", + "nancay": "xkcd:light green", + "srt": "xkcd:dark green", + "wsrt": "xkcd:light blue", + "lofar": "xkcd:dark red", + "lwa": "xkcd:dark red", + "mwa": "xkcd:dark red", + "meerkat": "xkcd:magenta", + "barycenter": "xkcd:black", + "geocenter": "xkcd:grey", + "space": "xkcd:light grey", + "other": "xkcd:yellow", + } + selected_color = "orange" + + obs_text = {} + for obs in obs_colors: + # try to get all of the capitalization special cases right + if obs in ["parkes", "jodrell", "arecibo", "nancay", "effelsberg"]: + obs_name = obs.capitalize() + elif obs in ["barycenter", "geocenter", "space", "other"]: + obs_name = obs + elif obs in ["gb"]: + obs_name = "Green Bank" + elif obs in ["meerkat"]: + obs_name = "MeerKAT" + else: + obs_name = obs.upper() + obs_text[ + obs + ] = f" {obs_colors[obs].replace('xkcd:','').capitalize()} = {obs_name}" + + def displayInfo(self): + outstr = '"Observatory" mode selected\n' + for obs in self.get_obs_mapping().values(): + outstr += self.obs_text[obs].replace("xkcd", "") + "\n" + outstr += f" {self.selected_color.capitalize()} = selected\n" + print(outstr) + + def plotColorMode(self): + """ + Plots application's residuals in proper color scheme. + """ + obsmap = self.get_obs_mapping() + alltoas = self.application.psr.all_toas + for obs, ourobs in obsmap.items(): + # group toa indices by observatory + toas = alltoas.get_obss() == obs + color = self.obs_colors[ourobs] + if self.application.yerrs is None: + self.application.plkAxes.scatter( + self.application.xvals[toas], + self.application.yvals[toas], + marker=".", + color=color, + ) + else: + self.application.plotErrorbar(toas, color=color) + # Now handle the selected TOAs + if self.application.yerrs is None: + self.application.plkAxes.scatter( + self.application.xvals[self.application.selected], + self.application.yvals[self.application.selected], + marker=".", + color=self.selected_color, + ) + else: + self.application.plotErrorbar( + self.application.selected, color=self.selected_color + ) + + +class JumpMode(ColorMode): + """Mode to color points according to jump""" + + def __init__(self, application): + super().__init__(application) + self.mode_name = "jump" + + def get_jumps(self): + """Return the jump objects for the `psr` model or an empty list + + Returns + ------- + list : + List of jump objects + """ + if self.application.psr.fitted: + model = self.application.psr.postfit_model + else: + model = self.application.psr.prefit_model + if "PhaseJump" not in model.components: + return [] + return model.get_jump_param_objects() + + jump_colors = named_colors + selected_color = "xkcd:orange" + + def displayInfo(self): + outstr = '"Jump" mode selected\n' + for jumpnum, jump in enumerate(self.get_jumps()): + # only use the number of colors - 1 to preserve orange for selected + color_number = jumpnum % (len(self.jump_colors) - 1) + color_name = self.jump_colors[color_number] + outstr += f" {color_name.replace('xkcd:','').capitalize()} = " + outstr += f"{jump.name}" + if jump.key is not None: + outstr += f" {jump.key}" + if jump.key_value is not None: + outstr += " " + " ".join(jump.key_value) + outstr += "\n" + outstr += ( + f" {self.selected_color.replace('xkcd:','').capitalize()} = selected\n" + ) + print(outstr) + + def plotColorMode(self): + """Plot the points with the desired coloring""" + alltoas = self.application.psr.all_toas + for jumpnum, jump in enumerate(self.get_jumps()): + color_number = jumpnum % (len(self.jump_colors) - 1) + color_name = self.jump_colors[color_number] + toas = jump.select_toa_mask(alltoas) + # group toa indices by jump + if self.application.yerrs is None: + self.application.plkAxes.scatter( + self.application.xvals[toas], + self.application.yvals[toas], + marker=".", + color=color_name, + ) + else: + self.application.plotErrorbar(toas, color=color_name) + # Now handle the selected TOAs + if self.application.yerrs is None: + self.application.plkAxes.scatter( + self.application.xvals[self.application.selected], + self.application.yvals[self.application.selected], + marker=".", + color=self.selected_color, + ) + else: + self.application.plotErrorbar( + self.application.selected, color=self.selected_color + ) diff --git a/pylk/pylk/constants.py b/pylk/pylk/constants.py index 4ee4a20..cbfd41c 100644 --- a/pylk/pylk/constants.py +++ b/pylk/pylk/constants.py @@ -10,16 +10,17 @@ import matplotlib as mpl PylkBanner = """ +----------------------------------------------+ | PINT | - | ==== ,~~~~. | - | Modern Pulsar Timing i====i_ | - | |cccc|_) | - | Brought to you by the |cccc| | - | NANOGrav collaboration `-==-' | + | ==== ,~~~~. | + | Modern Pulsar Timing i====i_ | + | |cccc|_) | + | Brought to you by the |cccc| | + | NANOGrav collaboration `-==-' | | | +----------------------------------------------+ """ + #winsize_with_jupyter = (1350, 550) #winsize_without_jupyter = (650, 550) winsize_with_jupyter = (1400, 700) @@ -58,11 +59,14 @@ mpl_rcParams_black = dict({ 'axes.facecolor': 'black', 'axes.edgecolor': 'white', 'axes.labelcolor': 'white', - 'axes.prop_cycle': mpl.cycler(color=['y', 'm', 'c', 'r', 'g', 'b', 'w']), + #'axes.prop_cycle': mpl.cycler(color=['y', 'm', 'c', 'r', 'g', 'b', 'w']), + 'axes.prop_cycle': mpl.cycler(color=['#8dd3c7', '#feffb3', '#bfbbd9', '#fa8174', '#81b1d2', '#fdb462', '#b3de69', '#bc82bd', '#ccebc4', '#ffed6f']), 'xtick.color': 'white', 'ytick.color': 'white', 'grid.color': 'grey', 'grid.linestyle': 'dashed', + 'grid.linewidth': '0.5', + 'grid.alpha': '0.5', 'figure.facecolor': 'black', 'figure.edgecolor': 'black', 'savefig.facecolor': 'black', diff --git a/pylk/pylk/plk.py b/pylk/pylk/plk.py index 5d8e174..b31ab80 100644 --- a/pylk/pylk/plk.py +++ b/pylk/pylk/plk.py @@ -77,7 +77,8 @@ import astropy.units as u import numpy as np import pint.pintk.pulsar as pulsar -import pint.pintk.colormodes as cm +from pylk import colormodes as cm +#import pint.pintk.colormodes as cm #from pylk import pulsar # Not used anymore from pylk import constants @@ -422,6 +423,7 @@ class PlkFitboxesWidget(QWidget): self.boxChecked(parchanged, bool(w.widget().checkState())) print("{0} set to {1}".format(parchanged, bool(w.widget().checkState()))) + class PlkRandomModelSelect(QFrame): """ Allows one to select whether to fit with random models or not @@ -563,7 +565,7 @@ class PlkColorModeBoxes(QWidget): self.group.addButton(radiobutton) self.layout.addWidget(radiobutton) self.radiobuttons.append(radiobutton) - + if mode.mode_name == "default": # default mode should be selected at start-up radiobutton.setChecked(True) @@ -1102,7 +1104,8 @@ class PlkWidget(QWidget): #rcP['savefig.facecolor'] = rgbcolor #rcP['savefig.edgecolor'] = rgbcolor - for key, value in rcP.items(): + #for key, value in rcP.items(): + for key, value in constants.mpl_rcParams_black.items(): mpl.rcParams[key] = value else: for key, value in constants.mpl_rcParams_black.items(): @@ -1538,7 +1541,8 @@ class PlkWidget(QWidget): mjd = m.conjunction(tt) pb = m.pb()[0].to_value("day") phs = (mjd - tt) / pb - self.plkAxes.plot([phs, phs], [ymin, ymax], "k-") + # TODO: Color + self.plkAxes.plot([phs, phs], [ymin, ymax], "w-") else: self.plkAxes.set_ylabel(plotlabels[self.yid]) @@ -1565,8 +1569,9 @@ class PlkWidget(QWidget): sort_inds = np.argsort(f_toas_plot) f_toas_plot = f_toas_plot[sort_inds] for i in range(len(rs)): + # TODO: Color self.plkAxes.plot( - f_toas_plot, rs[i][sort_inds] * scale, "-k", alpha=0.3 + f_toas_plot, rs[i][sort_inds] * scale, "-w", alpha=0.3 ) def determine_yaxis_units(self, miny, maxy): -- GitLab