GSoC 2020: glue-solar project 2.1

The second coding period is now officially halfway through. Since the end of the first coding period, I have been working on both the 1D Profile viewer and WCS autolinking. Since much has been discussed about the 1D Profile viewer of glue and now that it is finally working, let me take the opportunity to talk about the wcs-autolinking PR.

Problem statement: Originally the wcs-autolinking only worked for some cases, for example for the spatial axes but not for the temporal and wavelength axes in the scenario where the dimensions of the two wcs objects of the data cubes being matched up do not match. This is highly undesirable and needs generalizing while conforming to APE 14, where the issue of a shared Python interface for World Coordinate Systems is discussed.

The solution: So we rewrote the code. Most of the celestial code has been retained, with some new additions to add linking for the other dimensions. The code rewritten is as follows:

# Case for when the axes don't exactly match up
if not forwards or not backwards:
    # A generalized APE 14-compatible way
# Handle also the extra-spatial axes such as those of the time and wavelength dimensions
    if not wcs1.has_celestial or not wcs2.has_celestial:
raise IncompatibleWCS("Can't create WCS link between {0} and {1}".format(data1.label, data2.label))
    try:
wcs1_celestial = wcs1.celestial
wcs2_celestial = wcs2.celestial
wcs1_celestial_axis_physical_types = wcs1.celestial.world_axis_physical_types
wcs2_celestial_axis_physical_types = wcs2.celestial.world_axis_physical_types
print('wcs1.celestial.world_axis_physical_types', wcs1_celestial_axis_physical_types)
print('wcs2.celestial.world_axis_physical_types', wcs2_celestial_axis_physical_types)
except Exception:
raise IncompatibleWCS("Can't create WCS link between {0} and {1}".format(data1.label, data2.label))
    cids1 = data1.pixel_component_ids
cids1_celestial = [cids1[wcs1.wcs.naxis - wcs1.wcs.lng - 1],
cids1[wcs1.wcs.naxis - wcs1.wcs.lat - 1]]
slicing_axes_celestial1 = [cids1_celestial[0].axis, cids1_celestial[1].axis]
slicing_axes_celestial1 = sorted(slicing_axes_celestial1, key=str, reverse=False)
print('slicing_axes_celestial1', slicing_axes_celestial1)
    if wcs1_celestial.wcs.lng > wcs1_celestial.wcs.lat:
cids1_celestial = cids1_celestial[::-1]
    cids2 = data2.pixel_component_ids
cids2_celestial = [cids2[wcs2.wcs.naxis - wcs2.wcs.lng - 1],
cids2[wcs2.wcs.naxis - wcs2.wcs.lat - 1]]
slicing_axes_celestial2 = [cids2_celestial[0].axis, cids2_celestial[1].axis]
slicing_axes_celestial2 = sorted(slicing_axes_celestial2, key=str, reverse=False)
print('slicing_axes_celestial2', slicing_axes_celestial2)
    if wcs2_celestial.wcs.lng > wcs2_celestial.wcs.lat:
cids2_celestial = cids2_celestial[::-1]
    # Collect all apparently matching axes in two lists for the two wcs objects being linked up
wcs1_sliced_physical_types = wcs1_celestial_axis_physical_types
slicing_axes1 = slicing_axes_celestial1
wcs2_sliced_physical_types = wcs2_celestial_axis_physical_types
slicing_axes2 = slicing_axes_celestial2
for i, physical_type1 in enumerate(wcs1.world_axis_physical_types):
for j, physical_type2 in enumerate(wcs2.world_axis_physical_types):
if physical_type1 == physical_type2:
if physical_type1 not in wcs1_sliced_physical_types:
slicing_axes1.append(wcs1.world_n_dim - i - 1)
wcs1_sliced_physical_types.append(physical_type1)
if physical_type2 not in wcs2_sliced_physical_types:
slicing_axes2.append(wcs2.world_n_dim - j - 1)
wcs2_sliced_physical_types.append(physical_type2)
    slicing_axes1 = sorted(slicing_axes1, key=str, reverse=True)
slicing_axes2 = sorted(slicing_axes2, key=str, reverse=True)
    print('slicing_axes1', slicing_axes1)
print('slicing_axes2', slicing_axes2)
    print('wcs1_sliced_physical_types', wcs1_sliced_physical_types)
print('wcs2_sliced_physical_types', wcs2_sliced_physical_types)
    # Generate slices for the wcs slicing
slices1 = (slice(None),) * wcs1.world_n_dim
slices2 = (slice(None),) * wcs2.world_n_dim
    slices1 = sorted(list(slices1))
slices2 = sorted(list(slices2))
    for i in range(wcs1.world_n_dim):
if i in slicing_axes1:
pass
else:
slices1[i] = 0
    for i in range(wcs2.world_n_dim):
if i in slicing_axes2:
pass
else:
slices2[i] = 0
    wcs1_sliced = wcs1[tuple(slices1)]
wcs2_sliced = wcs2[tuple(slices2)]
    cids1 = data1.pixel_component_ids
cids1_sliced = [cids1[x] for x in slicing_axes1]
cids1_sliced = sorted(cids1_sliced, key=str, reverse=True)
    cids2 = data2.pixel_component_ids
cids2_sliced = [cids2[x] for x in slicing_axes2]
cids2_sliced = sorted(cids2_sliced, key=str, reverse=True)
    pixel_cids1, pixel_cids2, forwards, backwards = get_cids_and_functions(
wcs1_sliced, wcs2_sliced, cids1_sliced, cids2_sliced)
    self._physical_types_1 = wcs1_sliced_physical_types
self._physical_types_2 = wcs2_sliced_physical_types
    if pixel_cids1 is None:
raise IncompatibleWCS("Can't create WCS link between {0} and {1}".format(data1.label, data2.label))

After checking with the tests written previously the code was modified before it is confirmed with pytest that all CI tests are now passing.

Now we can link up wcs axes of the same physical types of two data cubes having different dimensions with no problems, illustrated as follows: