Skip to content

Commit

Permalink
Merge pull request #66 from transientskp/reimplement_dataaccessor
Browse files Browse the repository at this point in the history
Reimplement dataaccessor
  • Loading branch information
HannoSpreeuw authored Aug 16, 2024
2 parents 5a3336a + d163f9b commit 0566ae8
Show file tree
Hide file tree
Showing 13 changed files with 99 additions and 173 deletions.
2 changes: 1 addition & 1 deletion sourcefinder/accessors/aartfaaccasaimage.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@

class AartfaacCasaImage(CasaImage):
def __init__(self, url, plane=0, beam=None):
super(AartfaacCasaImage, self).__init__(url, plane=0, beam=None)
super().__init__(url, plane=0, beam=None)
table = casacore_table(self.url, ack=False)
self.taustart_ts = self.parse_taustartts(table)
self.telescope = table.getkeyword('coords')['telescope']
Expand Down
2 changes: 1 addition & 1 deletion sourcefinder/accessors/amicasaimage.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ class AmiCasaImage(CasaImage):
"""

def __init__(self, url, plane=0, beam=None):
super(AmiCasaImage, self).__init__(url, plane, beam)
super().__init__(url, plane, beam)
table = casacore_table(self.url, ack=False)
self.taustart_ts = self.parse_taustartts(table)
self.tau_time = 1 # Placeholder value until properly implemented
2 changes: 1 addition & 1 deletion sourcefinder/accessors/casaimage.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ class CasaImage(DataAccessor):
"""

def __init__(self, url, plane=0, beam=None):
super(CasaImage, self).__init__()
# super().__init__()
self.url = url

# we don't want the table as a property since it makes the accessor
Expand Down
144 changes: 72 additions & 72 deletions sourcefinder/accessors/dataaccessor.py
Original file line number Diff line number Diff line change
@@ -1,80 +1,80 @@
import logging
from math import degrees, sqrt, sin, pi, cos

from sourcefinder.accessors.requiredatts import RequiredAttributesMetaclass
import numpy
from math import degrees, sqrt, sin, pi, cos
from dataclasses import dataclass
from sourcefinder.utility.coordinates import WCS

logger = logging.getLogger(__name__)


class DataAccessor(object, metaclass=RequiredAttributesMetaclass):
_required_attributes = [
'beam',
'centre_ra',
'centre_decl',
'data',
'freq_bw',
'freq_eff',
'pixelsize',
'tau_time',
'taustart_ts',
'url',
'wcs',
]

def __init__(self):
# Sphinx only picks up the class docstring if it's under an __init__
# *le sigh*
"""
Base class for accessors used with
:class:`sourcefinder.image.ImageData`.
Data accessors provide a uniform way for the ImageData class (ie,
generic image representation) to access the various ways in which
images may be stored (FITS files, arrays in memory, potentially HDF5,
etc).
This class cannot be instantiated directly, but should be subclassed
and the abstract properties provided. Note that all abstract
properties are required to provide a valid accessor.
Additional properties may also be provided by subclasses. However,
TraP components are required to degrade gracefully in the absence of
this optional properties.
The required attributes are as follows:
Attributes:
beam (tuple): Restoring beam. Tuple of three floats:
semi-major axis (in pixels), semi-minor axis (pixels)
and position angle (radians).
centre_ra (float): Right ascension at the central pixel of the image.
Units of J2000 decimal degrees.
centre_decl (float): Declination at the central pixel of the image.
Units of J2000 decimal degrees.
data(numpy.ndarray): Two dimensional numpy.ndarray of floating point
pixel values.
(TODO: Definitive statement on orientation/transposing.)
freq_bw(float): The frequency bandwidth of this image in Hz.
freq_eff(float): Effective frequency of the image in Hz.
That is, the mean frequency of all the visibility data which
comprises this image.
pixelsize(tuple): (x, y) tuple representing the size of a pixel
along each axis in units of degrees.
tau_time(float): Total time on sky in seconds.
taustart_ts(float): Timestamp of the first integration which
constitutes part of this image. MJD in seconds.
url(string): A (string) URL representing the location of the image
at time of processing.
wcs(:class:`sourcefinder.utility.coordinates.WCS`): An instance of
:py:class:`sourcefinder.utility.coordinates.WCS`,
describing the mapping from data pixels to sky-coordinates.
The class also provides some common functionality:
static methods used for parsing datafiles, and an 'extract_metadata'
function which provides key info in a simple dict format.
"""

def extract_metadata(self):
@dataclass
class DataAccessor:
# Sphinx only picks up the class docstring if it's under an __init__
# *le sigh*
"""
Base class for accessors used with
:class:`sourcefinder.image.ImageData`.
Data accessors provide a uniform way for the ImageData class (ie,
generic image representation) to access the various ways in which
images may be stored (FITS files, arrays in memory, potentially HDF5,
etc).
This class cannot be instantiated directly, but should be subclassed
and the abstract properties provided. Note that all abstract
properties are required to provide a valid accessor.
Additional properties may also be provided by subclasses. However,
TraP components are required to degrade gracefully in the absence of
this optional properties.
The required attributes are as follows:
Attributes:
beam (tuple): Restoring beam. Tuple of three floats:
semi-major axis (in pixels), semi-minor axis (pixels)
and position angle (radians).
centre_ra (float): Right ascension at the central pixel of the image.
Units of J2000 decimal degrees.
centre_decl (float): Declination at the central pixel of the image.
Units of J2000 decimal degrees.
data(numpy.ndarray): Two dimensional numpy.ndarray of floating point
pixel values.
(TODO: Definitive statement on orientation/transposing.)
freq_bw(float): The frequency bandwidth of this image in Hz.
freq_eff(float): Effective frequency of the image in Hz.
That is, the mean frequency of all the visibility data which
comprises this image.
pixelsize(tuple): (x, y) tuple representing the size of a pixel
along each axis in units of degrees.
tau_time(float): Total time on sky in seconds.
taustart_ts(float): Timestamp of the first integration which
constitutes part of this image. MJD in seconds.
url(string): A (string) URL representing the location of the image
at time of processing.
wcs(:class:`sourcefinder.utility.coordinates.WCS`): An instance of
:py:class:`sourcefinder.utility.coordinates.WCS`,
describing the mapping from data pixels to sky-coordinates.
The class also provides some common functionality:
static methods used for parsing datafiles, and an 'extract_metadata'
function which provides key info in a simple dict format.
"""

beam: tuple
centre_ra: float
centre_decl: float
data: numpy.ndarray
freq_bw: float
freq_eff: float
pixelsize: tuple
tau_time: float
taustart_ts: float
url: str
wcs: WCS

def extract_metadata(self) -> dict:
"""
Massage the class attributes into a flat dictionary with
database-friendly values.
Expand Down Expand Up @@ -155,4 +155,4 @@ def degrees2pixels(bmaj, bmin, bpa, deltax, deltay):
(sin(pi * bpa / 180.) ** 2) / (deltay ** 2))
)
theta = pi * bpa / 180
return (semimaj, semimin, theta)
return semimaj, semimin, theta
3 changes: 1 addition & 2 deletions sourcefinder/accessors/fitsimage.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@

logger = logging.getLogger(__name__)


class FitsImage(DataAccessor):
"""
Use PyFITS to pull image data out of a FITS file.
Expand All @@ -21,7 +22,6 @@ class FitsImage(DataAccessor):
header.
"""
def __init__(self, url, plane=None, beam=None, hdu_index=0):
super(FitsImage, self).__init__()
self.url = url
self.header = self._get_header(hdu_index)
self.wcs = self.parse_coordinates()
Expand Down Expand Up @@ -69,7 +69,6 @@ def read_data(self, hdu_index, plane):
data = data.transpose()
return data


def parse_coordinates(self):
"""Returns a WCS object"""
header = self.header
Expand Down
11 changes: 8 additions & 3 deletions sourcefinder/accessors/fitsimageblob.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ class FitsImageBlob(FitsImage):
def __init__(self, hdulist, plane=None, beam=None, hdu_index=0):
# set the URL in case we need it during header parsing for error loggign
self.url = "AARTFAAC streaming image"
super(FitsImage, self).__init__()
# super(FitsImage, self).__init__()

self.header = self._get_header(hdulist, hdu_index)
self.wcs = self.parse_coordinates()
Expand All @@ -42,10 +42,15 @@ def __init__(self, hdulist, plane=None, beam=None, hdu_index=0):
if 'TELESCOP' in self.header:
self.telescope = self.header['TELESCOP']

def _get_header(self, hdulist, hdu_index):
def _get_header(self, *args):
hdulist = args[0]
hdu_index = args[1]
return hdulist[hdu_index].header

def read_data(self, hdulist, hdu_index, plane):
def read_data(self, *args):
hdulist = args[0]
hdu_index = args[1]
plane = args[2]
hdu = hdulist[hdu_index]
data = numpy.float64(hdu.data.squeeze())
if plane is not None and len(data.shape) > 2:
Expand Down
2 changes: 1 addition & 1 deletion sourcefinder/accessors/kat7casaimage.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ class Kat7CasaImage(CasaImage):
"""

def __init__(self, url, plane=0, beam=None):
super(Kat7CasaImage, self).__init__(url, plane, beam)
super().__init__(url, plane, beam)
table = casacore_table(self.url, ack=False)
self.taustart_ts = self.parse_taustartts(table)
self.tau_time = 1 # Placeholder value
20 changes: 10 additions & 10 deletions sourcefinder/accessors/lofaraccessor.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
from sourcefinder.accessors.dataaccessor import RequiredAttributesMetaclass
from dataclasses import dataclass


class LofarAccessor(object, metaclass=RequiredAttributesMetaclass):
@dataclass
class LofarAccessor():
"""
Additional metadata required for processing LOFAR images through QC
checks.
Expand All @@ -15,11 +16,10 @@ class LofarAccessor(object, metaclass=RequiredAttributesMetaclass):
subbandwidth(float): Width of a subband in Hz.
subbands(int): Number of subbands.
"""
_required_attributes = [
'antenna_set',
'ncore',
'nremote',
'nintl',
'subbandwidth',
'subbands',
]

antenna_set: str
ncore: int
nremote: int
nintl: int
subbandwidth: float
subbands: int
2 changes: 1 addition & 1 deletion sourcefinder/accessors/lofarcasaimage.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ class LofarCasaImage(CasaImage, LofarAccessor):
"""

def __init__(self, url, plane=0, beam=None):
super(LofarCasaImage, self).__init__(url, plane, beam)
super().__init__(url, plane, beam)
table = casacore_table(self.url, ack=False)
subtables = self.open_subtables(table)
self.taustart_ts = self.parse_taustartts(subtables)
Expand Down
2 changes: 1 addition & 1 deletion sourcefinder/accessors/lofarfitsimage.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

class LofarFitsImage(FitsImage, LofarAccessor):
def __init__(self, url, plane=False, beam=False, hdu=0):
super(LofarFitsImage, self).__init__(url, plane, beam, hdu)
super().__init__(url, plane, beam, hdu)
header = self._get_header(hdu)
self.antenna_set = header['ANTENNA']
self.ncore = header['NCORE']
Expand Down
1 change: 0 additions & 1 deletion sourcefinder/accessors/lofarhdf5image.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
class LofarHdf5Image(DataAccessor):
# Not currently instantiable; LOFAR HDF5 images are not in use
def __init__(self, source, plane=False, beam=False):
super(LofarHdf5Image, self).__init__() # Set defaults

self.plane = plane

Expand Down
77 changes: 0 additions & 77 deletions sourcefinder/accessors/requiredatts.py

This file was deleted.

4 changes: 2 additions & 2 deletions test/test_image.py
Original file line number Diff line number Diff line change
Expand Up @@ -244,7 +244,8 @@ def testForcedFitAtNans(self):
nanbox_radius *= boxsize_proportion

nandata[int(x0 - nanbox_radius):int(x0 + nanbox_radius + 1),
int(y0 - nanbox_radius):int(y0 + nanbox_radius + 1)] = float('nan')
int(y0 - nanbox_radius):int(y0 + nanbox_radius + 1)] = \
float('nan')

# Dump image data for manual inspection:
# import astropy.io.fits as fits
Expand All @@ -253,7 +254,6 @@ def testForcedFitAtNans(self):
# hdu = fits.PrimaryHDU((output_data).transpose())
# hdu.writeto('/tmp/nandata.fits',clobber=True)


nan_image = ImageData(nandata, beam=self.image.beam,
wcs=self.image.wcs)

Expand Down

0 comments on commit 0566ae8

Please sign in to comment.