diff --git a/CHANGELOG.md b/CHANGELOG.md index a21accbf9..79d6ed4b4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 * Added support to keep previously generated moment images ([#1202](https://github.com/CARTAvis/carta-backend/issues/1202)). * Added pugixml as a third-party library with the option PUGIXML_COMPACT enabled ([#1217](https://github.com/CARTAvis/carta-backend/issues/1217)). * Added automatically generated documentation with Doxygen ([#1215](https://github.com/CARTAvis/carta-backend/issues/1215)). +* Added support for loading swapped-axes image cubes ([#1178](https://github.com/CARTAvis/carta-backend/issues/1178)). ### Changed * Removed CASA CRTF parser for performance and annotation region support ([#1219](https://github.com/CARTAvis/carta-backend/issues/1219)). diff --git a/carta-protobuf b/carta-protobuf index c02470ad8..aeb52e8a8 160000 --- a/carta-protobuf +++ b/carta-protobuf @@ -1 +1 @@ -Subproject commit c02470ad85fcedd57bb9ff02b46d43a52a610834 +Subproject commit aeb52e8a87c80708665532a525af1fd42b9d807b diff --git a/src/FileList/FileExtInfoLoader.cc b/src/FileList/FileExtInfoLoader.cc index 432a47661..75befb3fb 100644 --- a/src/FileList/FileExtInfoLoader.cc +++ b/src/FileList/FileExtInfoLoader.cc @@ -196,11 +196,14 @@ bool FileExtInfoLoader::FillFileInfoFromImage(CARTA::FileInfoExtended& extended_ AddDataTypeEntry(extended_info, data_type); - int spectral_axis, depth_axis, stokes_axis; - if (_loader->FindCoordinateAxes(image_shape, spectral_axis, depth_axis, stokes_axis, message)) { + std::vector spatial_axes, render_axes; + int spectral_axis, stokes_axis, depth_axis; + if (_loader->FindCoordinateAxes(image_shape, spatial_axes, spectral_axis, stokes_axis, render_axes, depth_axis, message)) { + casacore::Vector axes_names; + AddShapeEntries( + extended_info, image_shape, spatial_axes, spectral_axis, stokes_axis, render_axes, depth_axis, axes_names); + // Computed entries for rendered image axes, depth axis (may not be spectral), stokes axis - std::vector render_axes = _loader->GetRenderAxes(); - AddShapeEntries(extended_info, image_shape, spectral_axis, depth_axis, stokes_axis, render_axes); AddComputedEntries(extended_info, image.get(), render_axes, use_image_for_entries); info_ok = true; } @@ -593,8 +596,10 @@ void FileExtInfoLoader::AddInitialComputedEntries(const std::string& hdu, CARTA: // Use header entries to determine computed entries casacore::IPosition shape; - int chan_axis(-1), depth_axis(-1), stokes_axis(-1); - std::vector spectral_ctypes = {"ENER", "VOPT", "ZOPT", "VELO", "VRAD", "BETA"}; + std::vector spatial_axes(2, -1); + int spectral_axis(-1), stokes_axis(-1), depth_axis(-1); + casacore::Vector axes_names(4, "NA"); + std::vector spectral_ctypes = {"FREQ", "WAV", "ENER", "VOPT", "ZOPT", "VELO", "VRAD", "BETA", "FELO"}; casacore::DataType data_type(casacore::DataType::TpFloat); for (int i = 0; i < extended_info.header_entries_size(); ++i) { @@ -620,16 +625,33 @@ void FileExtInfoLoader::AddInitialComputedEntries(const std::string& hdu, CARTA: std::string ctype_index(&entry_name.back()); int axis_num = stoi(ctype_index) - 1; auto entry_value = header_entry.value(); - std::transform(entry_value.begin(), entry_value.end(), entry_value.begin(), [](unsigned char c) { return std::toupper(c); }); - - if (entry_value == "STOKES") { - stokes_axis = axis_num; - } else if ((entry_value.find("FREQ") == 0) || (entry_value.find("WAV") != std::string::npos) || - (std::find(spectral_ctypes.begin(), spectral_ctypes.end(), entry_value) != spectral_ctypes.end())) { - chan_axis = axis_num; - if (chan_axis > 1) { - depth_axis = chan_axis; + + if (!entry_value.empty()) { + entry_value = entry_value.substr(0, entry_value.find("-", 0)); + std::transform( + entry_value.begin(), entry_value.end(), entry_value.begin(), [](unsigned char c) { return std::toupper(c); }); + + // Fill in axis names + if (axis_num >= 0 && axis_num < axes_names.size()) { + axes_names[axis_num] = entry_value; } + + // Assign axis numbers for different types + if (entry_value.find("RA") == 0 || entry_value.find("GLON") == 0 || entry_value.find("UU") == 0) { + spatial_axes[0] = axis_num; + } else if (entry_value.find("DEC") == 0 || entry_value.find("GLAT") == 0 || entry_value.find("VV") == 0) { + spatial_axes[1] = axis_num; + } else if (entry_value.find("STOKES") == 0) { + stokes_axis = axis_num; + } else if (std::any_of(spectral_ctypes.begin(), spectral_ctypes.end(), + [&](const std::string& key_word) { return (entry_value.find(key_word) != std::string::npos); })) { + spectral_axis = axis_num; + } + } + + // Depth axis is not the first two axes [0, 1], i.e., non-render axis that is not stokes (if any) + if (axis_num > 1 && axis_num != stokes_axis) { + depth_axis = axis_num; } } else if (entry_name.find("BITPIX") == 0) { auto value = header_entry.value(); @@ -643,7 +665,7 @@ void FileExtInfoLoader::AddInitialComputedEntries(const std::string& hdu, CARTA: } AddDataTypeEntry(extended_info, data_type); - AddShapeEntries(extended_info, shape, chan_axis, depth_axis, stokes_axis, render_axes); + AddShapeEntries(extended_info, shape, spatial_axes, spectral_axis, stokes_axis, render_axes, depth_axis, axes_names); if (compressed_fits) { compressed_fits->SetShape(shape); @@ -665,13 +687,15 @@ void FileExtInfoLoader::AddDataTypeEntry(CARTA::FileInfoExtended& extended_info, entry->set_entry_type(CARTA::EntryType::STRING); } -void FileExtInfoLoader::AddShapeEntries(CARTA::FileInfoExtended& extended_info, const casacore::IPosition& shape, int chan_axis, - int depth_axis, int stokes_axis, const std::vector& render_axes) { +void FileExtInfoLoader::AddShapeEntries(CARTA::FileInfoExtended& extended_info, const casacore::IPosition& shape, + const std::vector& spatial_axes, int spectral_axis, int stokes_axis, const std::vector& render_axes, int depth_axis, + casacore::Vector& axes_names) { // Set fields/header entries for shape: dimensions, width, height, depth, stokes int num_dims(shape.size()); int width(shape(render_axes[0])); int height(shape(render_axes[1])); int depth(depth_axis >= 0 ? shape(depth_axis) : 1); + int channels(spectral_axis >= 0 ? shape(spectral_axis) : 1); int stokes(stokes_axis >= 0 ? shape(stokes_axis) : 1); extended_info.set_dimensions(num_dims); @@ -680,17 +704,56 @@ void FileExtInfoLoader::AddShapeEntries(CARTA::FileInfoExtended& extended_info, extended_info.set_depth(depth); extended_info.set_stokes(stokes); + auto* axes_numbers_info = extended_info.mutable_axes_numbers(); + // Change to 1-based axis indices + axes_numbers_info->set_spatial_x(spatial_axes[0] + 1); + axes_numbers_info->set_spatial_y(spatial_axes[1] + 1); + axes_numbers_info->set_spectral(spectral_axis + 1); + axes_numbers_info->set_stokes(stokes_axis + 1); + axes_numbers_info->set_depth(depth_axis + 1); + + if (axes_names.empty()) { + // Set axis names with respect to axis numbers 1~4 + axes_names.assign(casacore::Vector(4, "NA")); + for (int i = 0; i < extended_info.header_entries_size(); ++i) { + auto header_entry = extended_info.header_entries(i); + auto entry_name = header_entry.name(); + if (entry_name.find("CTYPE") == 0) { + auto entry_value = header_entry.value(); + if (!entry_value.empty()) { + entry_value = entry_value.substr(0, entry_value.find("-", 0)); + if (entry_name.back() == '1') { + axes_names[0] = entry_value; + } else if (entry_name.back() == '2') { + axes_names[1] = entry_value; + } else if (entry_name.back() == '3') { + axes_names[2] = entry_value; + } else if (entry_name.back() == '4') { + axes_names[3] = entry_value; + } + } + } + } + } + + // In case if the stokes axis name is not available from the header info + if (stokes_axis > -1 && axes_names[stokes_axis] == "NA") { + axes_names[stokes_axis] = "STOKES"; + } + // shape computed_entry std::string shape_string; switch (num_dims) { case 2: - shape_string = fmt::format("[{}, {}]", shape(0), shape(1)); + shape_string = fmt::format("[{}, {}] ({}, {})", shape(0), shape(1), axes_names[0], axes_names[1]); break; case 3: - shape_string = fmt::format("[{}, {}, {}]", shape(0), shape(1), shape(2)); + shape_string = + fmt::format("[{}, {}, {}] ({}, {}, {})", shape(0), shape(1), shape(2), axes_names[0], axes_names[1], axes_names[2]); break; case 4: - shape_string = fmt::format("[{}, {}, {}, {}]", shape(0), shape(1), shape(2), shape(3)); + shape_string = fmt::format("[{}, {}, {}, {}] ({}, {}, {}, {})", shape(0), shape(1), shape(2), shape(3), axes_names[0], + axes_names[1], axes_names[2], axes_names[3]); break; } auto shape_entry = extended_info.add_computed_entries(); @@ -698,23 +761,21 @@ void FileExtInfoLoader::AddShapeEntries(CARTA::FileInfoExtended& extended_info, shape_entry->set_value(shape_string); shape_entry->set_entry_type(CARTA::EntryType::STRING); - if (chan_axis >= 0) { + if (spectral_axis >= 0) { // header entry for number of channels - unsigned int nchan = shape(chan_axis); auto entry = extended_info.add_computed_entries(); entry->set_name("Number of channels"); - entry->set_value(std::to_string(nchan)); + entry->set_value(std::to_string(channels)); entry->set_entry_type(CARTA::EntryType::INT); - entry->set_numeric_value(nchan); + entry->set_numeric_value(channels); } if (stokes_axis >= 0) { // header entry for number of stokes - unsigned int nstokes = shape(stokes_axis); auto entry = extended_info.add_computed_entries(); entry->set_name("Number of polarizations"); - entry->set_value(std::to_string(nstokes)); + entry->set_value(std::to_string(stokes)); entry->set_entry_type(CARTA::EntryType::INT); - entry->set_numeric_value(nstokes); + entry->set_numeric_value(stokes); } } @@ -781,10 +842,11 @@ void FileExtInfoLoader::AddComputedEntries(CARTA::FileInfoExtended& extended_inf entry->set_value(format_coords); entry->set_entry_type(CARTA::EntryType::STRING); - bool coord0IsDir(coord0.isConform("deg")), coord1IsDir(coord1.isConform("deg")); - if (coord0IsDir || coord1IsDir) { + bool is_coord0_dir(coord0.isConform("deg")), is_coord1_dir(coord1.isConform("deg")); + if (is_coord0_dir || is_coord1_dir) { // Reference coord(s) converted to deg - std::string ref_coords_deg = ConvertCoordsToDeg(coord0, coord1); + std::string ref_coords_deg = fmt::format("[{}, {}]", ConvertCoordsToDeg(axis_names(display_axis0), coord0), + ConvertCoordsToDeg(axis_names(display_axis1), coord1)); // Add ref coords in deg entry = extended_info.add_computed_entries(); entry->set_name("Image ref coords (deg)"); @@ -1079,7 +1141,7 @@ void FileExtInfoLoader::AddComputedEntriesFromHeaders( bool q1IsDir(q1.isConform("deg")), q2IsDir(q2.isConform("deg")); if (q1IsDir || q2IsDir) { // Reference coord(s) converted to deg - std::string ref_coords_deg = ConvertCoordsToDeg(q1, q2); + std::string ref_coords_deg = fmt::format("[{}, {}]", ConvertCoordsToDeg(coord_name1, q1), ConvertCoordsToDeg(coord_name2, q2)); auto comp_entry = extended_info.add_computed_entries(); comp_entry->set_name("Image ref coords (deg)"); comp_entry->set_value(ref_coords_deg); @@ -1243,22 +1305,21 @@ std::string FileExtInfoLoader::MakeAngleString(const std::string& type, double v return fmt::format("{:.6g} {}", val, unit); } - casacore::Quantity quant1(val, unit); + casacore::Quantity quant1(val, unit), pi2(360, "deg"); + if (type.find("Longitude") != std::string::npos && quant1.get("deg").getValue() < 0) { + quant1 += pi2; + } casacore::MVAngle mva(quant1); return mva.string(format, 10); } -std::string FileExtInfoLoader::ConvertCoordsToDeg(const casacore::Quantity& coord0, const casacore::Quantity& coord1) { +std::string FileExtInfoLoader::ConvertCoordsToDeg(const std::string& type, const casacore::Quantity& coord) { // If possible, convert quantities to degrees. Return formatted string - casacore::Quantity coord0_deg(coord0), coord1_deg(coord1); - if (coord0.isConform("deg")) { - coord0_deg = coord0.get("deg"); - } - if (coord1.isConform("deg")) { - coord1_deg = coord1.get("deg"); + casacore::Quantity coord_deg(coord), pi2(360, "deg"); + if (coord.isConform("deg")) { + coord_deg = (type == "Longitude" && coord.get("deg").getValue() < 0) ? (coord + pi2).get("deg") : coord.get("deg"); } - - return fmt::format("[{}, {}]", coord0_deg, coord1_deg); + return fmt::format("{}", coord_deg); } std::string FileExtInfoLoader::ConvertIncrementToArcsec(const casacore::Quantity& inc0, const casacore::Quantity& inc1) { @@ -1330,7 +1391,7 @@ void FileExtInfoLoader::AddCoordRanges( if (coord_system.hasDirectionCoordinate()) { auto direction_coord = coord_system.directionCoordinate(); if (direction_coord.referenceValue().size() == 2) { - casacore::Vector direction_axes = coord_system.directionAxesNumbers(); + casacore::Vector spatial_axes = coord_system.directionAxesNumbers(); casacore::Vector axis_names = coord_system.worldAxisNames(); casacore::Vector pixels(2, 0); casacore::Vector world(2, 0); @@ -1357,8 +1418,8 @@ void FileExtInfoLoader::AddCoordRanges( } }; - double x_max_pixel = image_shape[direction_axes[0]] - 1; - double y_max_pixel = image_shape[direction_axes[1]] - 1; + double x_max_pixel = image_shape[spatial_axes[0]] - 1; + double y_max_pixel = image_shape[spatial_axes[1]] - 1; get_xy_minmax(0, 0); get_xy_minmax(x_max_pixel, y_max_pixel); get_xy_minmax(0, y_max_pixel); @@ -1373,26 +1434,29 @@ void FileExtInfoLoader::AddCoordRanges( std::string y_end = direction_coord.format(units, casacore::Coordinate::DEFAULT, y_max, 1, true, true); // Set x and y coordinate names - if (axis_names(0) == "Right Ascension") { - axis_names(0) = "RA"; - } else if (axis_names(0) == "Longitude") { - axis_names(0) = "LON"; - } - if (axis_names(1) == "Declination") { - axis_names(1) = "DEC"; - } else if (axis_names(1) == "Latitude") { - axis_names(1) = "LAT"; + if (spatial_axes[0] > -1 && spatial_axes[0] < axis_names.size()) { + if (axis_names(spatial_axes[0]) == "Right Ascension") { + axis_names(spatial_axes[0]) = "RA"; + } else if (axis_names(spatial_axes[0]) == "Longitude") { + axis_names(spatial_axes[0]) = "LON"; + } + auto* x_entry = extended_info.add_computed_entries(); + x_entry->set_name(fmt::format("{} range", axis_names(spatial_axes[0]))); + x_entry->set_value(fmt::format("[{}, {}]", x_start, x_end)); + x_entry->set_entry_type(CARTA::EntryType::STRING); } - auto* x_entry = extended_info.add_computed_entries(); - x_entry->set_name(fmt::format("{} range", axis_names(0))); - x_entry->set_value(fmt::format("[{}, {}]", x_start, x_end)); - x_entry->set_entry_type(CARTA::EntryType::STRING); - - auto* y_entry = extended_info.add_computed_entries(); - y_entry->set_name(fmt::format("{} range", axis_names(1))); - y_entry->set_value(fmt::format("[{}, {}]", y_start, y_end)); - y_entry->set_entry_type(CARTA::EntryType::STRING); + if (spatial_axes[1] > -1 && spatial_axes[1] < axis_names.size()) { + if (axis_names(spatial_axes[1]) == "Declination") { + axis_names(spatial_axes[1]) = "DEC"; + } else if (axis_names(spatial_axes[1]) == "Latitude") { + axis_names(spatial_axes[1]) = "LAT"; + } + auto* y_entry = extended_info.add_computed_entries(); + y_entry->set_name(fmt::format("{} range", axis_names(spatial_axes[1]))); + y_entry->set_value(fmt::format("[{}, {}]", y_start, y_end)); + y_entry->set_entry_type(CARTA::EntryType::STRING); + } } } diff --git a/src/FileList/FileExtInfoLoader.h b/src/FileList/FileExtInfoLoader.h index a02892736..23e3bf80a 100644 --- a/src/FileList/FileExtInfoLoader.h +++ b/src/FileList/FileExtInfoLoader.h @@ -50,8 +50,9 @@ class FileExtInfoLoader { // Computed entries void AddDataTypeEntry(CARTA::FileInfoExtended& extended_info, casacore::DataType data_type); - void AddShapeEntries(CARTA::FileInfoExtended& extended_info, const casacore::IPosition& shape, int chan_axis, int depth_axis, - int stokes_axis, const std::vector& render_axes); + void AddShapeEntries(CARTA::FileInfoExtended& extended_info, const casacore::IPosition& shape, const std::vector& spatial_axes, + int spectral_axis, int stokes_axis, const std::vector& render_axes, int depth_axis, + casacore::Vector& axes_names); void AddInitialComputedEntries(const std::string& hdu, CARTA::FileInfoExtended& extended_info, const std::string& filename, const std::vector& render_axes, CompressedFits* compressed_fits = nullptr); void AddComputedEntries(CARTA::FileInfoExtended& extended_info, casacore::ImageInterface* image, @@ -66,7 +67,7 @@ class FileExtInfoLoader { std::string MakeAngleString(const std::string& type, double val, const std::string& unit); // Convert Quantities and return formatted string - std::string ConvertCoordsToDeg(const casacore::Quantity& coord0, const casacore::Quantity& coord1); + std::string ConvertCoordsToDeg(const std::string& type, const casacore::Quantity& coord); std::string ConvertIncrementToArcsec(const casacore::Quantity& inc0, const casacore::Quantity& inc1); void GetCoordNames(std::string& ctype1, std::string& ctype2, std::string& radesys, std::string& coord_name1, std::string& coord_name2, diff --git a/src/Frame/Frame.cc b/src/Frame/Frame.cc index 9a47316f8..fc72191b2 100644 --- a/src/Frame/Frame.cc +++ b/src/Frame/Frame.cc @@ -68,18 +68,16 @@ Frame::Frame(uint32_t session_id, std::shared_ptr loader, const std: // Get shape and axis values from the loader std::string log_message; - if (!_loader->FindCoordinateAxes(_image_shape, _spectral_axis, _z_axis, _stokes_axis, log_message)) { + std::vector spatial_axes, render_axes; + if (!_loader->FindCoordinateAxes(_image_shape, spatial_axes, _spectral_axis, _stokes_axis, render_axes, _z_axis, log_message)) { _open_image_error = fmt::format("Cannot determine file shape. {}", log_message); spdlog::error("Session {}: {}", session_id, _open_image_error); _valid = false; return; } - // Determine which axes are rendered, e.g. for pV images - std::vector render_axes = _loader->GetRenderAxes(); _x_axis = render_axes[0]; _y_axis = render_axes[1]; - _width = _image_shape(_x_axis); _height = _image_shape(_y_axis); _depth = (_z_axis >= 0 ? _image_shape(_z_axis) : 1); diff --git a/src/Frame/Frame.h b/src/Frame/Frame.h index f531c3606..831b5f32c 100644 --- a/src/Frame/Frame.h +++ b/src/Frame/Frame.h @@ -279,7 +279,8 @@ class Frame { // Shape and axis info: X, Y, Z, Stokes casacore::IPosition _image_shape; - int _x_axis, _y_axis, _z_axis, _spectral_axis, _stokes_axis; + int _x_axis, _y_axis, _z_axis; // X and Y are render axes, Z is depth axis (non-render axis) that is not stokes (if any) + int _spectral_axis, _stokes_axis; int _z_index, _stokes_index; // current index size_t _width, _height, _depth, _num_stokes; diff --git a/src/ImageData/FileLoader.cc b/src/ImageData/FileLoader.cc index a17bc8e21..95fb6e892 100644 --- a/src/ImageData/FileLoader.cc +++ b/src/ImageData/FileLoader.cc @@ -175,9 +175,11 @@ std::shared_ptr FileLoader::GetCoordinateSystem(cons return std::make_shared(); } -bool FileLoader::FindCoordinateAxes(casacore::IPosition& shape, int& spectral_axis, int& z_axis, int& stokes_axis, std::string& message) { +bool FileLoader::FindCoordinateAxes(casacore::IPosition& shape, std::vector& spatial_axes, int& spectral_axis, int& stokes_axis, + std::vector& render_axes, int& z_axis, std::string& message) { // Return image shape and axes for image. Spectral axis may or may not be z axis. // All parameters are return values. + spatial_axes.assign(2, -1); spectral_axis = -1; z_axis = -1; stokes_axis = -1; @@ -201,15 +203,33 @@ bool FileLoader::FindCoordinateAxes(casacore::IPosition& shape, int& spectral_ax return false; } - // Determine which axes will be rendered - std::vector render_axes = GetRenderAxes(); + // Get spectral and stokes axis + spectral_axis = _coord_sys->spectralAxisNumber(); + stokes_axis = _coord_sys->polarizationAxisNumber(); + + // Set render axes are the first two axes that are not stokes + render_axes.resize(0); + for (int i = 0; i < _num_dims && render_axes.size() < 2; ++i) { + if (i != stokes_axis) { + render_axes.push_back(i); + } + } + _width = shape(render_axes[0]); _height = shape(render_axes[1]); _image_plane_size = _width * _height; - // Spectral and stokes axis - spectral_axis = _coord_sys->spectralAxisNumber(); - stokes_axis = _coord_sys->polarizationAxisNumber(); + // Find spatial axes + if (_coord_sys->hasDirectionCoordinate()) { + auto tmp_axes = _coord_sys->directionAxesNumbers(); + spatial_axes[0] = tmp_axes[0]; + spatial_axes[1] = tmp_axes[1]; + } else if (_coord_sys->hasLinearCoordinate()) { + auto tmp_axes = _coord_sys->linearAxesNumbers(); + for (int i = 0; i < casacore::min(tmp_axes.size(), 2); ++i) { // Assume the first two linear axes are spatial axes, if any + spatial_axes[i] = tmp_axes[i]; + } + } // 2D image if (_num_dims == 2) { @@ -282,59 +302,6 @@ bool FileLoader::FindCoordinateAxes(casacore::IPosition& shape, int& spectral_ax return true; } -std::vector FileLoader::GetRenderAxes() { - // Determine which axes will be rendered - std::vector axes; - - if (!_render_axes.empty()) { - axes = _render_axes; - return axes; - } - - // Default unless PV image - axes.assign({0, 1}); - - if (_image_shape.size() > 2) { - // Normally, use direction axes - if (_coord_sys->hasDirectionCoordinate()) { - casacore::Vector dir_axes = _coord_sys->directionAxesNumbers(); - axes[0] = dir_axes[0]; - axes[1] = dir_axes[1]; - } else if (_coord_sys->hasLinearCoordinate()) { - // Check for PV image: usually [Linear, Spectral] axes but could be reversed - // Returns -1 if no spectral axis - int spectral_axis = _coord_sys->spectralAxisNumber(); - - if (spectral_axis >= 0) { - // Find valid (not -1) linear axes - std::vector valid_axes; - if (spectral_axis == 0) { // reversed - valid_axes.push_back(spectral_axis); - } - - casacore::Vector lin_axes = _coord_sys->linearAxesNumbers(); - for (auto axis : lin_axes) { - if (axis >= 0) { - valid_axes.push_back(axis); - } - } - - if (spectral_axis > 0) { // not reversed - valid_axes.push_back(spectral_axis); - } - - // One linear + spectral axis = pV image - if (valid_axes.size() == 2) { - axes = valid_axes; - } - } - } - } - - _render_axes = axes; - return axes; -} - bool FileLoader::GetSlice(casacore::Array& data, const StokesSlicer& stokes_slicer) { StokesSource stokes_source = stokes_slicer.stokes_source; casacore::Slicer slicer = stokes_slicer.slicer; diff --git a/src/ImageData/FileLoader.h b/src/ImageData/FileLoader.h index 3c2c4bb36..b38c24197 100644 --- a/src/ImageData/FileLoader.h +++ b/src/ImageData/FileLoader.h @@ -23,8 +23,6 @@ namespace carta { -class Frame; - struct StokesSlicer { StokesSource stokes_source; casacore::Slicer slicer; @@ -78,8 +76,8 @@ class FileLoader { // Image shape and coordinate system axes casacore::IPosition GetShape(); std::shared_ptr GetCoordinateSystem(const StokesSource& stokes_source = StokesSource()); - bool FindCoordinateAxes(casacore::IPosition& shape, int& spectral_axis, int& z_axis, int& stokes_axis, std::string& message); - std::vector GetRenderAxes(); // Determine axes used for image raster data + bool FindCoordinateAxes(casacore::IPosition& shape, std::vector& spatial_axes, int& spectral_axis, int& stokes_axis, + std::vector& render_axes, int& z_axis, std::string& message); // Slice image data (with mask applied) bool GetSlice(casacore::Array& data, const StokesSlicer& stokes_slicer); @@ -152,7 +150,6 @@ class FileLoader { size_t _num_dims, _image_plane_size; size_t _width, _height, _depth, _num_stokes; int _z_axis, _stokes_axis; - std::vector _render_axes; std::shared_ptr _coord_sys; bool _has_pixel_mask; casacore::DataType _data_type; diff --git a/src/ImageData/StokesFilesConnector.cc b/src/ImageData/StokesFilesConnector.cc index 19431dff0..5a0c3b280 100644 --- a/src/ImageData/StokesFilesConnector.cc +++ b/src/ImageData/StokesFilesConnector.cc @@ -300,6 +300,7 @@ bool StokesFilesConnector::StokesFilesValid(std::string& err, int& stokes_axis) } casacore::IPosition ref_shape(0); + std::vector ref_spatial_axes, ref_render_axes; int ref_spectral_axis = -1; int ref_z_axis = -1; int ref_stokes_axis = -1; @@ -307,13 +308,15 @@ bool StokesFilesConnector::StokesFilesValid(std::string& err, int& stokes_axis) for (auto& loader : _loaders) { casacore::IPosition shape; + std::vector spatial_axes, render_axes; int spectral_axis; int z_axis; if (ref_index == 0) { - loader.second->FindCoordinateAxes(ref_shape, ref_spectral_axis, ref_z_axis, ref_stokes_axis, err); + loader.second->FindCoordinateAxes( + ref_shape, ref_spatial_axes, ref_spectral_axis, ref_stokes_axis, ref_render_axes, ref_z_axis, err); } else { - loader.second->FindCoordinateAxes(shape, spectral_axis, z_axis, stokes_axis, err); + loader.second->FindCoordinateAxes(shape, spatial_axes, spectral_axis, stokes_axis, render_axes, z_axis, err); if ((ref_shape.nelements() != shape.nelements()) || (ref_shape != shape) || (ref_spectral_axis != spectral_axis) || (ref_stokes_axis != stokes_axis)) { err = "Image shapes or axes are not consistent!"; diff --git a/test/TestFileInfo.cc b/test/TestFileInfo.cc index c9d2a4870..9625aa978 100644 --- a/test/TestFileInfo.cc +++ b/test/TestFileInfo.cc @@ -115,7 +115,7 @@ class FileExtInfoLoaderTest : public ::testing::Test { } else if (computed_entries.name() == "HDU") { CheckHeaderEntry(computed_entries, "0", CARTA::EntryType::STRING); } else if (computed_entries.name() == "Shape") { - CheckHeaderEntry(computed_entries, "[6, 6, 5, 1]", CARTA::EntryType::STRING); + CheckHeaderEntry(computed_entries, "[6, 6, 5, 1] (RA, DEC, FREQ, STOKES)", CARTA::EntryType::STRING); } else if (computed_entries.name() == "Number of channels") { CheckHeaderEntry(computed_entries, "5", CARTA::EntryType::INT, 5); } else if (computed_entries.name() == "Number of polarizations") {