Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

improve admin for aperture photometry results #147

Merged
merged 2 commits into from
Oct 22, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 32 additions & 2 deletions iop4admin/modeladmins/aperphotresult.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,26 @@

class AdminAperPhotResult(admin.ModelAdmin):
model = AperPhotResult
list_display = ['id', 'get_telescope', 'get_instrument', 'get_datetime', 'get_src_name', 'get_src_type', 'get_fwhm', 'get_aperpix', 'get_reducedfit', 'get_obsmode', 'pairs', 'get_rotangle', 'get_src_type', 'get_flux_counts', 'get_flux_counts_err', 'get_bkg_flux_counts', 'get_bkg_flux_counts_err', 'modified']
list_display = ['id', 'get_telescope', 'get_instrument', 'get_datetime', 'get_src_name', 'get_src_type', 'get_fwhm', 'get_aperpix', 'get_r_in', 'get_r_out', 'get_reducedfit', 'get_obsmode', 'pairs', 'get_rotangle', 'get_src_type', 'get_flux_counts', 'get_flux_counts_err', 'get_bkg_flux_counts', 'get_bkg_flux_counts_err', 'get_image_preview', 'modified']
readonly_fields = [field.name for field in AperPhotResult._meta.fields]
search_fields = ['id', 'reducedfit__instrument', 'astrosource__name', 'astrosource__srctype', 'reducedfit__id']
list_filter = ['reducedfit__instrument', 'astrosource__srctype', 'reducedfit__epoch__telescope', 'reducedfit__obsmode']


def get_urls(self):
urls = super().get_urls()
my_urls = [
path('preview/<int:pk>', self.admin_site.admin_view(self.view_preview), name=f"iop4api_{self.model._meta.model_name}_preview"),
]
return my_urls + urls

def view_preview(self, request, pk):

if ((fit := self.model.objects.filter(id=pk).first()) is None):
morcuended marked this conversation as resolved.
Show resolved Hide resolved
return HttpResponseNotFound()

imgbytes = fit.get_img()
return HttpResponse(imgbytes, content_type="image/png")

@admin.display(description="TELESCOPE")
def get_telescope(self, obj):
Expand Down Expand Up @@ -70,6 +84,18 @@ def get_aperpix(self, obj):
return "-"
return f"{obj.aperpix:.1f}"

@admin.display(description="r_in")
def get_r_in(self, obj):
if obj.r_in is None:
return "-"
return f"{obj.r_in:.1f}"

@admin.display(description="r_out")
def get_r_out(self, obj):
if obj.r_out is None:
return "-"
return f"{obj.r_out:.1f}"

@admin.display(description="flux_counts")
def get_flux_counts(self, obj):
if obj.flux_counts is None:
Expand Down Expand Up @@ -97,4 +123,8 @@ def get_bkg_flux_counts_err(self, obj):
return "-"
else:
return f"{obj.bkg_flux_counts_err:.1f}"


@admin.display(description="img")
def get_image_preview(self, obj, allow_tags=True):
morcuended marked this conversation as resolved.
Show resolved Hide resolved
url_img_preview = reverse(f"iop4admin:iop4api_{self.model._meta.model_name}_preview", args=[obj.id])
return format_html(rf"<img src='{url_img_preview}' width='64' height='64' />")
13 changes: 11 additions & 2 deletions iop4admin/modeladmins/photopolresult.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@

class AdminPhotoPolResult(admin.ModelAdmin):
model = PhotoPolResult
list_display = ['id', 'get_telescope', 'get_juliandate', 'get_datetime', 'get_src_name', 'get_src_type', 'get_reducedfits', 'obsmode', 'band', 'exptime', 'get_mag', 'get_mag_err', 'get_p', 'get_p_err', 'get_chi', 'get_chi_err', 'get_aperpix', 'get_aperas', 'get_flags', 'modified']
list_display = ['id', 'get_telescope', 'get_juliandate', 'get_datetime', 'get_src_name', 'get_src_type', 'get_reducedfits', 'obsmode', 'band', 'exptime', 'get_mag', 'get_mag_err', 'get_p', 'get_p_err', 'get_chi', 'get_chi_err', 'get_aperpix', 'get_aperas', 'get_flags', 'get_aperphots', 'modified']
readonly_fields = [field.name for field in PhotoPolResult._meta.fields]
search_fields = ['id', 'astrosource__name', 'astrosource__srctype', 'epoch__night']
ordering = ['-juliandate']
Expand Down Expand Up @@ -86,4 +86,13 @@ def get_aperas(self, obj):

@admin.display(description='Status')
def get_flags(self, obj):
return ", ".join(list(obj.flag_labels))
return ", ".join(list(obj.flag_labels))

@admin.display(description="AperPhots")
def get_aperphots(self, obj):
self.allow_tags = True

ids_str_L = [str(apres.id) for apres in obj.aperphotresults.all()]
a_href = reverse('iop4admin:%s_%s_changelist' % (AperPhotResult._meta.app_label, AperPhotResult._meta.model_name)) + "?id__in=%s" % ",".join(ids_str_L)
a_text = ", ".join(ids_str_L)
return mark_safe(f'<a href="{a_href}">{a_text}</a>')
94 changes: 93 additions & 1 deletion iop4lib/db/aperphotresult.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,20 @@
from django.db import models

# other imports
import os
import os
morcuended marked this conversation as resolved.
Show resolved Hide resolved
import io
import numpy as np
import matplotlib as mplt
import matplotlib.pyplot as plt
from astropy.visualization.mpl_normalize import ImageNormalize
from astropy.visualization import LogStretch
from astropy.nddata import Cutout2D
from photutils.aperture import CircularAperture, CircularAnnulus

# iop4lib imports
from ..enums import *
from iop4lib.instruments.instrument import Instrument

# logging
import logging
Expand Down Expand Up @@ -53,7 +64,7 @@ class Meta:
models.UniqueConstraint(fields=['reducedfit', 'astrosource', 'aperpix', 'r_in', 'r_out', 'pairs'], name='unique_aperphotresult')
]

# repr and str
# repr and str

def __repr__(self):
return f'{self.__class__.__name__}.objects.get(id={self.id!r})'
Expand All @@ -75,3 +86,84 @@ def create(cls, reducedfit, astrosource, aperpix, pairs, **kwargs):

result.save()

return result

@property
def filedpropdir(self):
return os.path.join(iop4conf.datadir, "aperphotresults", str(self.id))

def get_img(self, force_rebuild=True, **kwargs):
"""
Build an image preview (png) of the aperture and annulus over the source.

If called with default arguments (no kwargs) it will try to load from the disk,
except if called with force_rebuild.

When called with default arguments (no kwargs), if rebuilt, it will save the image to disk.
"""

wcs = self.reducedfit.wcs1 if self.pairs == 'O' else self.reducedfit.wcs2

if self.reducedfit.has_pairs:
cutout_size = np.ceil(2.2*np.linalg.norm(Instrument.by_name(self.reducedfit.instrument).disp_sign_mean))
else:
cutout_size = np.ceil(1.3*self.r_out)

cutout = Cutout2D(self.reducedfit.mdata, (self.x_px, self.y_px), (cutout_size, cutout_size), wcs)

width = kwargs.get('width', 256)
height = kwargs.get('height', 256)
normtype = kwargs.get('norm', "log")
vmin = kwargs.get('vmin', np.quantile(cutout.data.compressed(), 0.3))
vmax = kwargs.get('vmax', np.quantile(cutout.data.compressed(), 0.99))
a = kwargs.get('a', 10)

fpath = os.path.join(self.filedpropdir, "img_preview_image.png")

if len(kwargs) == 0 and not force_rebuild:
if os.path.isfile(fpath) and os.path.getmtime(self.filepath) < os.path.getmtime(fpath):
with open(fpath, 'rb') as f:
return f.read()

cmap = plt.cm.gray.copy()
cmap.set_bad(color='red')
cmap.set_under(color='black')
cmap.set_over(color='white')

if normtype == "log":
norm = ImageNormalize(cutout.data.compressed(), vmin=vmin, vmax=vmax, stretch=LogStretch(a=a))
elif normtype == "logstretch":
norm = ImageNormalize(stretch=LogStretch(a=a))


buf = io.BytesIO()

fig = mplt.figure.Figure(figsize=(width/100, height/100), dpi=iop4conf.mplt_default_dpi)
ax = fig.subplots()

wcs_px_pos = self.astrosource.coord.to_pixel(cutout.wcs)
xy_px_pos = cutout.to_cutout_position((self.x_px, self.y_px))
ap = CircularAperture(xy_px_pos, r=self.aperpix)
annulus = CircularAnnulus(xy_px_pos, r_in=self.r_in, r_out=self.r_out)

ax.imshow(cutout.data, cmap=cmap, origin='lower', norm=norm)
ax.plot(wcs_px_pos[0], wcs_px_pos[1], 'rx', label='WCS')
ax.plot(xy_px_pos[0], xy_px_pos[1], 'bo', label='Photometry')
ap.plot(ax, color='blue', lw=2, alpha=1)
annulus.plot(ax, color='green', lw=2, alpha=1)

ax.axis('off')
fig.savefig(buf, format='png', bbox_inches='tight', pad_inches=0)
fig.clf()

buf.seek(0)
imgbytes = buf.read()

# if it was rebuilt, save it to disk if it is the default image settings.
if len(kwargs) == 0:
if not os.path.exists(self.filedpropdir):
os.makedirs(self.filedpropdir)
with open(fpath, 'wb') as f:
f.write(imgbytes)

return imgbytes
2 changes: 2 additions & 0 deletions iop4lib/db/masterbias.py
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,8 @@ def create(cls,
if auto_merge_to_db:
mb.save()

return mb

def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.auto_merge_to_db = True
Expand Down
15 changes: 10 additions & 5 deletions iop4lib/instruments/cafos.py
Original file line number Diff line number Diff line change
Expand Up @@ -383,11 +383,16 @@ def compute_relative_polarimetry(cls, polarimetry_group):
# save the results

result = PhotoPolResult.create(reducedfits=polarimetry_group,
astrosource=astrosource,
reduction=REDUCTIONMETHODS.RELPOL,
mag_inst=mag_inst, mag_inst_err=mag_inst_err, mag_zp=mag_zp, mag_zp_err=mag_zp_err,
flux_counts=flux_mean, p=P, p_err=dP, chi=Theta, chi_err=dTheta,
aperpix=aperpix)
astrosource=astrosource,
reduction=REDUCTIONMETHODS.RELPOL,
mag_inst=mag_inst, mag_inst_err=mag_inst_err,
mag_zp=mag_zp, mag_zp_err=mag_zp_err,
flux_counts=flux_mean,
p=P, p_err=dP,
chi=Theta, chi_err=dTheta,
aperpix=aperpix)

result.aperphotresults.set(qs, clear=True)

photopolresult_L.append(result)

Expand Down
4 changes: 3 additions & 1 deletion iop4lib/instruments/dipol.py
Original file line number Diff line number Diff line change
Expand Up @@ -1188,7 +1188,9 @@ def _get_p_and_chi(Qr, Ur, dQr, dUr):
p=P, p_err=dP, chi=chi, chi_err=dchi,
_q_nocorr=Qr_uncorr, _u_nocorr=Ur_uncorr, _p_nocorr=P_uncorr, _chi_nocorr=chi_uncorr,
aperpix=aperpix)


result.aperphotresults.set(aperphotresults, clear=True)

photopolresult_L.append(result)

# 3. Save results
Expand Down
20 changes: 17 additions & 3 deletions iop4lib/instruments/instrument.py
Original file line number Diff line number Diff line change
Expand Up @@ -526,6 +526,8 @@ def compute_aperture_photometry(cls, redf, aperpix, r_in, r_out):

error = calc_total_error(img, bkg.background_rms, cls.gain_e_adu)

apres_L = list()

for astrosource in redf.sources_in_field.all():
for pairs, wcs in (('O', redf.wcs1), ('E', redf.wcs2)) if redf.has_pairs else (('O',redf.wcs),):

Expand All @@ -548,14 +550,18 @@ def compute_aperture_photometry(cls, redf, aperpix, r_in, r_out):
flux_counts = ap_stats.sum - annulus_stats.mean*ap_stats.sum_aper_area.value # TODO: check if i should use mean!
flux_counts_err = ap_stats.sum_err

AperPhotResult.create(reducedfit=redf,
apres = AperPhotResult.create(reducedfit=redf,
astrosource=astrosource,
aperpix=aperpix,
r_in=r_in, r_out=r_out,
x_px=centroid_px_pos[0], y_px=centroid_px_pos[1],
pairs=pairs,
bkg_flux_counts=bkg_flux_counts, bkg_flux_counts_err=bkg_flux_counts_err,
flux_counts=flux_counts, flux_counts_err=flux_counts_err)

apres_L.append(apres)

return apres_L

@classmethod
def compute_relative_photometry(cls, redf: 'ReducedFit') -> None:
Expand Down Expand Up @@ -590,9 +596,15 @@ def compute_relative_photometry(cls, redf: 'ReducedFit') -> None:

for astrosource in redf.sources_in_field.all():

result = PhotoPolResult.create(reducedfits=[redf], astrosource=astrosource, reduction=REDUCTIONMETHODS.RELPHOT)
qs_aperphotresult = AperPhotResult.objects.filter(reducedfit=redf, astrosource=astrosource, aperpix=aperpix, pairs="O")

aperphotresult = AperPhotResult.objects.get(reducedfit=redf, astrosource=astrosource, aperpix=aperpix, pairs="O")
if not qs_aperphotresult.exists():
logger.error(f"{redf}: no aperture photometry for source {astrosource.name} found, skipping relative photometry.")
continue

aperphotresult = qs_aperphotresult.first()

result = PhotoPolResult.create(reducedfits=[redf], astrosource=astrosource, reduction=REDUCTIONMETHODS.RELPHOT)

result.aperpix = aperpix
result.bkg_flux_counts = aperphotresult.bkg_flux_counts
Expand Down Expand Up @@ -630,6 +642,8 @@ def compute_relative_photometry(cls, redf: 'ReducedFit') -> None:
result.mag_zp = None
result.mag_zp_err = None

result.aperphotresults.set([aperphotresult], clear=True)

result.save()

photopolresult_L.append(result)
Expand Down
Loading