//# Copyright (C) 2009 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library is distributed in the hope that it will be useful, but WITHOUT //# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or //# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# #ifndef IMAGEANALYSIS_IMAGEMETADATABASE_TCC #define IMAGEANALYSIS_IMAGEMETADATABASE_TCC #include <imageanalysis/ImageAnalysis/ImageMetaDataBase.h> #include <casacore/casa/aips.h> #include <casacore/casa/Arrays/ArrayLogical.h> #include <casacore/casa/Containers/ValueHolder.h> #include <casacore/casa/Quanta/QuantumHolder.h> #include <casacore/casa/Utilities/DataType.h> #include <casacore/images/Images/ImageSummary.h> #include <casacore/images/Images/ImageStatistics.h> #include <casacore/measures/Measures/MeasureHolder.h> #include <casacore/casa/Utilities/Regex.h> #include <iostream> #include <iomanip> #define _ORIGINB LogOrigin("ImageMetaDataBase", __func__, WHERE) using namespace std; using namespace casacore; namespace casa { template <class T> ImageMetaDataBase<T>::ImageMetaDataBase(SPCIIT image) : _image(image), _log(), _shape() { ThrowIf(! _image, "image cannot be NULL"); _shape = _image->shape(); } template <class T> Record ImageMetaDataBase<T>::_makeHeader() const { Record header; header.define(ImageMetaDataConstants::_IMTYPE, _getImType()); header.define(ImageMetaDataConstants::_OBJECT, _getObject()); const auto& csys = _getCoords(); if (csys.hasDirectionCoordinate()) { const DirectionCoordinate& dc = csys.directionCoordinate(); String equinox = MDirection::showType( dc.directionType() ); header.define(ImageMetaDataConstants::_EQUINOX, _getEquinox()); header.define(ImageMetaDataConstants::_PROJECTION, _getProjection()); } header.define(ImageMetaDataConstants::_OBSDATE, _getEpochString()); header.define(ImageMetaDataConstants::MASKS, _getMasks()); header.define(ImageMetaDataConstants::_OBSERVER, _getObserver()); header.define(ImageMetaDataConstants::_SHAPE, _getShape().asVector()); header.define(ImageMetaDataConstants::_TELESCOPE, _getTelescope()); header.define(ImageMetaDataConstants::_BUNIT, _getBrightnessUnit()); if (csys.hasSpectralAxis()) { const SpectralCoordinate& sc = csys.spectralCoordinate(); header.define(ImageMetaDataConstants::_RESTFREQ, sc.restFrequencies()); header.define( ImageMetaDataConstants::_REFFREQTYPE , _getRefFreqType() ); } const auto& info = _getInfo(); if (info.hasSingleBeam()) { GaussianBeam beam = _getBeam(); header.defineRecord( ImageMetaDataConstants::_BEAMMAJOR, QuantumHolder(beam.getMajor()).toRecord() ); header.defineRecord( ImageMetaDataConstants::_BEAMMINOR, QuantumHolder(beam.getMinor()).toRecord() ); header.defineRecord( ImageMetaDataConstants::_BEAMPA, QuantumHolder(beam.getPA(true)).toRecord() ); } else if (info.hasMultipleBeams()) { String error; Record rec; info.toRecord(error, rec); static const String recName = "perplanebeams"; Record beamRec = rec.asRecord(recName); beamRec.defineRecord( "median area beam", info.getBeamSet().getMedianAreaBeam().toRecord() ); header.defineRecord(recName, beamRec); } auto cdelt = _getIncrements(); auto units = _getAxisUnits(); auto crpix = _getRefPixel(); auto crval = _getRefValue(); auto types = _getAxisNames(); header.merge(_getStatistics()); for (uInt i=0; i<cdelt.size(); ++i) { auto iString = String::toString(i + 1); auto delt = ImageMetaDataConstants::_CDELT + iString; header.define(delt, cdelt[i].getValue()); auto unit = ImageMetaDataConstants::_CUNIT + iString; header.define(unit, units[i]); auto pix = ImageMetaDataConstants::_CRPIX + iString; header.define(pix, crpix[i]); auto val = ImageMetaDataConstants::_CRVAL + iString; header.define(val, crval[i].getValue()); auto type = ImageMetaDataConstants::_CTYPE + iString; header.define(type, types[i]); } return header; } template <class T> const TableRecord ImageMetaDataBase<T>::_miscInfo() const { return _image->miscInfo(); } template <class T> CoordinateSystem ImageMetaDataBase<T>::coordsys( const std::vector<Int>& pixelAxes ) const { // Recover CoordinateSytem into a Record auto cSys = _getCoords(); if (pixelAxes.empty()) { return cSys; } Record rec; CoordinateSystem cSys2; // Fish out the coordinate of the desired axes uInt j = 0; const Int nPixelAxes = cSys.nPixelAxes(); Vector<uInt> coordinates(cSys.nCoordinates(), uInt(0)); Int coord, axisInCoord; for (const auto& axis: pixelAxes) { ThrowIf ( axis < 0 || axis >= nPixelAxes, "Specified zero-based pixel axis " + String::toString(axis) + " is not a valid pixel axis" ); cSys.findPixelAxis(coord, axisInCoord, uInt(axis)); ThrowIf( coord < 0, "Zero-based pixel axis " + String::toString(axis) + " has been removed" ); coordinates(coord)++; // Copy desired coordinate (once) if (coordinates(coord) == 1) { cSys2.addCoordinate(cSys.coordinate(coord)); } } // Find mapping. Says where world axis i in cSys is in cSys2 Vector<Int> worldAxisMap, worldAxisTranspose; Vector<Bool> refChange; ThrowIf( ! cSys2.worldMap(worldAxisMap, worldAxisTranspose, refChange, cSys), "Error finding world map because " + cSys2.errorMessage() ); // Generate list of world axes to keep Vector<Int> keepList(cSys.nWorldAxes()); Vector<Double> worldReplace; j = 0; for (const auto& axis: pixelAxes) { if (axis >= 0 && axis < nPixelAxes) { Int worldAxis = cSys.pixelAxisToWorldAxis(uInt(axis)); ThrowIf( worldAxis < 0, "World axis corresponding to zero-based pixel axis " + String::toString(axis) + " has been removed" ); keepList(j++) = worldAxisMap(worldAxis); } } // Remove unwanted world (and pixel) axes. Better would be to just // remove the pixel axes and leave the world axes there... if (j > 0) { keepList.resize(j, true); CoordinateUtil::removeAxes(cSys2, worldReplace, keepList, false); } // Copy the ObsInfo cSys2.setObsInfo(cSys.obsInfo()); return cSys2; } template <class T> DataType ImageMetaDataBase<T>::dataType() const { return _image->dataType(); } template <class T> Record* ImageMetaDataBase<T>::getBoundingBox( const Record& region ) const { const auto& csys = _getCoords(); const auto shape = _getShape(); const unique_ptr<ImageRegion> pRegion( ImageRegion::fromRecord( nullptr, csys, shape, region ) ); LatticeRegion latRegion = pRegion->toLatticeRegion( csys, shape ); Slicer sl = latRegion.slicer(); IPosition blc(sl.start()); IPosition trc(sl.end()); IPosition inc(sl.stride()); IPosition length(sl.length()); std::unique_ptr<Record> outRec(new Record()); outRec->define("blc", blc.asVector()); outRec->define("trc", trc.asVector()); outRec->define("inc", inc.asVector()); outRec->define("bbShape", (trc - blc + 1).asVector()); outRec->define("regionShape", length.asVector()); outRec->define("imageShape", shape.asVector()); outRec->define("blcf", CoordinateUtil::formatCoordinate(blc, csys)); // 0-rel for use in C++ outRec->define("trcf", CoordinateUtil::formatCoordinate(trc, csys)); return outRec.release(); } template <class T> ValueHolder ImageMetaDataBase<T>::getFITSValue(const String& key) const { String c = key; c.downcase(); const TableRecord info = _miscInfo(); if (c == ImageMetaDataConstants::_BUNIT) { return ValueHolder(_getBrightnessUnit()); } else if ( c.startsWith(ImageMetaDataConstants::_CDELT) || c.startsWith(ImageMetaDataConstants::_CRPIX) || c.startsWith(ImageMetaDataConstants::_CRVAL) || c.startsWith(ImageMetaDataConstants::_CTYPE) || c.startsWith(ImageMetaDataConstants::_CUNIT) ) { String prefix = c.substr(0, 5); uInt n = _getAxisNumber(c); if (prefix == ImageMetaDataConstants::_CDELT) { return ValueHolder( QuantumHolder( _getIncrements()[n-1] ).toRecord() ); } else if (prefix == ImageMetaDataConstants::_CRPIX) { return ValueHolder(_getRefPixel()[n-1]); } else if (prefix == ImageMetaDataConstants::_CRVAL) { if (_getCoords().polarizationAxisNumber(false) == (Int)(n-1)) { return ValueHolder( _getStokes() ); } else { return ValueHolder( QuantumHolder(_getRefValue()[n-1]).toRecord() ); } } else if (prefix == ImageMetaDataConstants::_CTYPE) { return ValueHolder(_getAxisNames()[n-1]); } else if (prefix == ImageMetaDataConstants::_CUNIT) { return ValueHolder(_getAxisUnits()[n-1]); } } else if (c == ImageMetaDataConstants::_EQUINOX) { return ValueHolder(_getEquinox()); } else if (c == ImageMetaDataConstants::_IMTYPE) { return ValueHolder(_getImType()); } else if (c == ImageMetaDataConstants::MASKS) { return ValueHolder(_getMasks()); } else if (c == ImageMetaDataConstants::_OBJECT) { return ValueHolder(_getObject()); } else if (c == ImageMetaDataConstants::_OBSDATE || c == ImageMetaDataConstants::_EPOCH) { return ValueHolder(_getEpochString()); } else if (c == ImageMetaDataConstants::_OBSERVER) { return ValueHolder(_getObserver()); } else if (c == ImageMetaDataConstants::_PROJECTION) { return ValueHolder(_getProjection()); } else if (c == ImageMetaDataConstants::_REFFREQTYPE) { return ValueHolder(_getRefFreqType()); } else if (c == ImageMetaDataConstants::_RESTFREQ) { return ValueHolder( QuantumHolder(_getRestFrequency()).toRecord() ); } else if (c == ImageMetaDataConstants::_SHAPE) { return ValueHolder(_getShape().asVector()); } else if (c == ImageMetaDataConstants::_TELESCOPE) { return ValueHolder(_getTelescope()); } else if ( c == ImageMetaDataConstants::_BEAMMAJOR || c == ImageMetaDataConstants::_BEAMMINOR || c == ImageMetaDataConstants::_BEAMPA || c == ImageMetaDataConstants::_BMAJ || c == ImageMetaDataConstants::_BMIN || c == ImageMetaDataConstants::_BPA ) { GaussianBeam beam = _getBeam(); if (c == ImageMetaDataConstants::_BEAMMAJOR || c == ImageMetaDataConstants::_BMAJ) { return ValueHolder(QuantumHolder(beam.getMajor()).toRecord()); } else if (c == ImageMetaDataConstants::_BEAMMINOR || c == ImageMetaDataConstants::_BMIN) { return ValueHolder(QuantumHolder(beam.getMinor()).toRecord()); } else { return ValueHolder(QuantumHolder(beam.getPA()).toRecord()); } } else if ( c == ImageMetaDataConstants::_DATAMIN || c == ImageMetaDataConstants::_DATAMAX || c == ImageMetaDataConstants::_MINPIXPOS || c == ImageMetaDataConstants::_MINPOS || c == ImageMetaDataConstants::_MAXPIXPOS || c == ImageMetaDataConstants::_MAXPOS ) { Record x = _getStatistics(); if (c == ImageMetaDataConstants::_DATAMIN || c == ImageMetaDataConstants::_DATAMAX) { auto dt = dataType(); if (dt == TpFloat) { Float val; x.get(c, val); return ValueHolder(val); } else if (dt == TpComplex) { Complex val; x.get(c, val); return ValueHolder(val); } else if (dt == TpDouble) { Double val; x.get(c, val); return ValueHolder(val); } else if (dt == TpDComplex) { DComplex val; x.get(c, val); return ValueHolder(val); } else { ThrowCc("Logic error"); } } else if (c == ImageMetaDataConstants::_MINPOS || c == ImageMetaDataConstants::_MAXPOS) { return ValueHolder(x.asString(c)); } else if (c == ImageMetaDataConstants::_MINPIXPOS || c == ImageMetaDataConstants::_MAXPIXPOS) { return ValueHolder(x.asArrayInt(c)); } } else if ( info.isDefined(key) || info.isDefined(c) ) { String x = info.isDefined(key) ? key : c; switch (info.type(info.fieldNumber(x))) { case TpString: return ValueHolder(info.asString(x)); break; case TpInt: return ValueHolder(info.asInt(x)); break; case TpDouble: return ValueHolder(info.asDouble(x)); break; case TpRecord: // allow fall through case TpQuantity: { return ValueHolder(info.asRecord(x)); break; } default: ostringstream os; os << info.type(info.fieldNumber(x)); ThrowCc( "Unhandled data type " + os.str() + " for user defined type. Send us a bug report" ); } } ThrowCc( "Unknown keyword " + key + ". If you are trying to use a key name from " "the imhead summary dictionary, note that some keys in " "mode='put'/'get' are different from mode='summary'. Please see imhead " "description in the CASA online documentation for complete details." ); return ValueHolder(); } template <class T> uInt ImageMetaDataBase<T>::_ndim() const { return _image->ndim(); } template <class T> uInt ImageMetaDataBase<T>::_getAxisNumber( const String& key ) const { uInt n = 0; string sre = key.substr(0, 5) + "[0-9]+"; Regex myre(Regex::makeCaseInsensitive(sre)); if (key.find(myre) != String::npos) { n = String::toInt(key.substr(5)); uInt ndim = _ndim(); ThrowIf( n == 0, "The FITS convention is that axes " "are 1-based. Therefore, " + key + " is not a valid " "FITS keyword specification" ); ThrowIf( n > ndim, "This image only has " + String::toString(ndim) + " axes." ); } else { ThrowCc("Unsupported key " + key); } return n; } template <class T> String ImageMetaDataBase<T>::_getEpochString() const { return MVTime(_getObsDate().getValue()).string(MVTime::YMD, 12); } template <class T> IPosition ImageMetaDataBase<T>::_getShape() const { if (_shape.empty()) { _shape = _image->shape(); } return _shape; } template <class T> void ImageMetaDataBase<T>::_fieldToLog( const Record& header,const String& field, Int precision ) const { _log << " -- " << field << ": "; if (header.isDefined(field)) { DataType type = header.type(header.idToNumber(field)); if (precision >= 0) { _log.output() << setprecision(precision); } switch (type) { case TpArrayDouble: { _log << header.asArrayDouble(field); break; } case TpArrayInt: { _log << header.asArrayInt(field); break; } case TpArrayString: { _log << header.asArrayString(field); break; } case TpDouble: { _log << header.asDouble(field); break; } case TpRecord: { Record r = header.asRecord(field); QuantumHolder qh; String error; if (qh.fromRecord(error, r) && qh.isQuantity()) { Quantity q = qh.asQuantity(); _log << q.getValue() << q.getUnit(); } else { _log << "Logic Error: Don't know how to deal with records of this type " << LogIO::EXCEPTION; } break; } case TpString: { _log << header.asString(field); break; } default: { _log << "Logic Error: Unsupported type " << type << LogIO::EXCEPTION; break; } } } else { _log << "Not found"; } _log << LogIO::POST; } template <class T> void ImageMetaDataBase<T>::_toLog(const Record& header) const { _log << _ORIGINB << "General --" << LogIO::POST; _fieldToLog(header, ImageMetaDataConstants::_IMTYPE); _fieldToLog(header, ImageMetaDataConstants::_OBJECT); _fieldToLog(header, ImageMetaDataConstants::_EQUINOX); _fieldToLog(header, ImageMetaDataConstants::_OBSDATE); _fieldToLog(header, ImageMetaDataConstants::_OBSERVER); _fieldToLog(header, ImageMetaDataConstants::_PROJECTION); if (header.isDefined(ImageMetaDataConstants::_RESTFREQ)) { _log << " -- " << ImageMetaDataConstants::_RESTFREQ << ": "; _log.output() << std::fixed << std::setprecision(1); _log << header.asArrayDouble(ImageMetaDataConstants::_RESTFREQ) << LogIO::POST; } _fieldToLog(header, ImageMetaDataConstants::_REFFREQTYPE); _fieldToLog(header, ImageMetaDataConstants::_TELESCOPE); _fieldToLog(header, ImageMetaDataConstants::_BEAMMAJOR, 12); _fieldToLog(header, ImageMetaDataConstants::_BEAMMINOR, 12); _fieldToLog(header, ImageMetaDataConstants::_BEAMPA, 12); _fieldToLog(header, ImageMetaDataConstants::_BUNIT); _fieldToLog(header, ImageMetaDataConstants::MASKS); _fieldToLog(header, ImageMetaDataConstants::_SHAPE); _fieldToLog(header, ImageMetaDataConstants::_DATAMIN); _fieldToLog(header, ImageMetaDataConstants::_DATAMAX); _fieldToLog(header, ImageMetaDataConstants::_MINPOS); _fieldToLog(header, ImageMetaDataConstants::_MINPIXPOS); _fieldToLog(header, ImageMetaDataConstants::_MAXPOS); _fieldToLog(header, ImageMetaDataConstants::_MAXPIXPOS); uInt i = 1; _log << LogIO::NORMAL << "axes --" << LogIO::POST; while (true) { String iString = String::toString(i); String key = ImageMetaDataConstants::_CTYPE + iString; if (! header.isDefined(key)) { break; } _log << " -- " << key << ": " << header.asString(key) << LogIO::POST; String unit = ImageMetaDataConstants::_CUNIT + iString; i++; } i = 1; _log << LogIO::NORMAL << ImageMetaDataConstants::_CRPIX << " --" << LogIO::POST; while (true) { String iString = String::toString(i); String key = ImageMetaDataConstants::_CRPIX + iString; if (! header.isDefined(key)) { break; } _log.output() << std::fixed << std::setprecision(1); _log << " -- " << key << ": " << header.asDouble(key) << LogIO::POST; i++; } i = 1; _log << LogIO::NORMAL << ImageMetaDataConstants::_CRVAL << " --" << LogIO::POST; while (true) { String iString = String::toString(i); String key = ImageMetaDataConstants::_CRVAL + iString; if (! header.isDefined(key)) { break; } _log << " -- " << key << ": "; ostringstream x; Double val = header.asDouble(key); x << val; String unit = ImageMetaDataConstants::_CUNIT + iString; if (header.isDefined(unit)) { x << header.asString(unit); } String valunit = x.str(); if (header.isDefined(unit)) { String myunit = header.asString(unit); if (header.asString(unit).empty()) { String ctype = ImageMetaDataConstants::_CTYPE + iString; if ( header.isDefined(ctype) && header.asString(ctype) == "Stokes" ) { valunit = "['" + Stokes::name((Stokes::StokesTypes)((Int)val)) + "']"; } } else { String tmp = _doStandardFormat(val, myunit); if (! tmp.empty()) { valunit = tmp; } } } _log << valunit << LogIO::POST; i++; } i = 1; _log << LogIO::NORMAL << ImageMetaDataConstants::_CDELT << " --" << LogIO::POST; while (true) { String iString = String::toString(i); String key = ImageMetaDataConstants::_CDELT + iString; if (! header.isDefined(key)) { break; } _log << " -- " << key << ": "; Double val = header.asDouble(key); String unit = ImageMetaDataConstants::_CUNIT + iString; String myunit; if (header.isDefined(unit)) { myunit = header.asString(unit); } ostringstream x; x << val << myunit; String valunit = x.str(); if (header.isDefined(unit)) { String myunit = header.asString(unit); if (! header.asString(unit).empty()) { String tmp = _doStandardFormat(val, myunit); if (! tmp.empty()) { valunit = tmp; } } } _log << valunit << LogIO::POST; i++; } i = 1; _log << LogIO::NORMAL << "units --" << LogIO::POST; while (true) { String iString = String::toString(i); String key = ImageMetaDataConstants::_CUNIT + iString; if (! header.isDefined(key)) { break; } _log << " -- " << key << ": " << header.asString(key) << LogIO::POST; String unit = ImageMetaDataConstants::_CUNIT + iString; i++; } } template <class T> String ImageMetaDataBase<T>::_doStandardFormat( Double value, const String& unit ) const { String valunit; try { Quantity q(1, unit); if (q.isConform(Quantity(1, "rad"))) { // to dms valunit = MVAngle(Quantity(value, unit)).string(MVAngle::CLEAN, 9) + "deg.min.sec"; } else if (unit == "Hz") { ostringstream x; x << std::fixed << std::setprecision(1); x << value << "Hz"; valunit = x.str(); } } catch (const AipsError& x) {} return valunit; } template <class T> uInt ImageMetaDataBase<T>::nChannels() const { const CoordinateSystem csys = _getCoords(); if (! csys.hasSpectralAxis()) { return 0; } return _getShape()[csys.spectralAxisNumber()]; } template <class T> Bool ImageMetaDataBase<T>::isChannelNumberValid( const uInt chan ) const { if (! _getCoords().hasSpectralAxis()) { return false; } return (chan < nChannels()); } template <class T> uInt ImageMetaDataBase<T>::nStokes() const { const CoordinateSystem& csys = _getCoords(); if (! csys.hasPolarizationCoordinate()) { return 0; } return _getShape()[csys.polarizationAxisNumber()]; } template <class T> Int ImageMetaDataBase<T>::stokesPixelNumber( const String& stokesString) const { Int pixNum = _getCoords().stokesPixelNumber(stokesString); if (pixNum >= (Int)nStokes()) { pixNum = -1; } return pixNum; } template <class T> String ImageMetaDataBase<T>::_getProjection() const { const CoordinateSystem csys = _getCoords(); if (! csys.hasDirectionCoordinate()) { return ""; } const DirectionCoordinate dc = csys.directionCoordinate(); Projection proj = dc.projection(); if (proj.type() == Projection::SIN) { Vector<Double> pars = proj.parameters(); if (dc.isNCP()) { ostringstream os; os << "SIN (" << pars << "): NCP"; return os.str(); } else if(pars.size() == 2 && (anyNE(pars, 0.0))) { // modified SIN ostringstream os; os << "SIN (" << pars << ")"; return os.str(); } } return proj.name(); } template <class T> String ImageMetaDataBase<T>::stokesAtPixel( const uInt pixel ) const { const CoordinateSystem& csys = _getCoords(); if (! csys.hasPolarizationCoordinate() || pixel >= nStokes()) { return ""; } return csys.stokesAtPixel(pixel); } template <class T> Bool ImageMetaDataBase<T>::isStokesValid( const String& stokesString ) const { if (! _getCoords().hasPolarizationCoordinate()) { return false; } Int stokesPixNum = stokesPixelNumber(stokesString); return stokesPixNum >= 0 && stokesPixNum < (Int)nStokes(); } template <class T> Vector<Int> ImageMetaDataBase<T>::directionShape() const { Vector<Int> dirAxesNums = _getCoords().directionAxesNumbers(); if (dirAxesNums.nelements() == 0) { return Vector<Int>(); } Vector<Int> dirShape(2); IPosition shape = _getShape(); dirShape[0] = shape[dirAxesNums[0]]; dirShape[1] = shape[dirAxesNums[1]]; return dirShape; } template <class T> Bool ImageMetaDataBase<T>::areChannelAndStokesValid( String& message, const uInt chan, const String& stokesString ) const { ostringstream os; Bool areValid = true; if (! isChannelNumberValid(chan)) { os << "Zero-based channel number " << chan << " is too large. There are only " << nChannels() << " spectral channels in this image."; areValid = false; } if (! isStokesValid(stokesString)) { if (! areValid) { os << " and "; } os << "Stokes parameter " << stokesString << " is not in image"; areValid = false; } if (! areValid) { message = os.str(); } return areValid; } template <class T> Record ImageMetaDataBase<T>::_calcStats() const { return _calcStatsT(_image); } template <class T> Record ImageMetaDataBase<T>::_calcStatsT( std::shared_ptr<const ImageInterface<T> > image ) const { Record x; if (! isReal(image->dataType())) { // the min and max and associated positions // cannot be calculated for complex images return x; } ImageStatistics<T> stats(*image); Array<typename NumericTraits<T>::PrecisionType> min; stats.getStatistic(min, LatticeStatsBase::MIN); if (min.size() == 0) { // image is completely masked return x; } x.define(ImageMetaDataConstants::_DATAMIN, min(IPosition(min.ndim(), 0))); Array<typename NumericTraits<T>::PrecisionType> max; stats.getStatistic(max, LatticeStatsBase::MAX); x.define(ImageMetaDataConstants::_DATAMAX, max(IPosition(max.ndim(), 0))); IPosition minPixPos, maxPixPos; stats.getMinMaxPos(minPixPos, maxPixPos); x.define(ImageMetaDataConstants::_MINPIXPOS, minPixPos.asVector()); x.define(ImageMetaDataConstants::_MAXPIXPOS, maxPixPos.asVector()); const auto& csys = _getCoords(); Vector<Double> minPos = csys.toWorld(minPixPos); Vector<Double> maxPos = csys.toWorld(maxPixPos); String minFormat, maxFormat; uInt ndim = csys.nPixelAxes(); Int spAxis = csys.spectralAxisNumber(); for (uInt i=0; i<ndim; i++) { Int worldAxis = csys.pixelAxisToWorldAxis(i); String foundUnit; minFormat += csys.format( foundUnit, Coordinate::DEFAULT, minPos[i], worldAxis ); maxFormat += csys.format( foundUnit, Coordinate::DEFAULT, maxPos[i], worldAxis ); if ((Int)i == spAxis) { minFormat += foundUnit; maxFormat += foundUnit; } if (i != ndim-1) { minFormat += " "; maxFormat += " "; } } x.define(ImageMetaDataConstants::_MINPOS, minFormat); x.define(ImageMetaDataConstants::_MAXPOS, maxFormat); return x; } template <class T> Record ImageMetaDataBase<T>::toWorld( const Vector<Double>& pixel, const String& format, Bool doVelocity, const String& dirFrame, const String& freqFrame ) const { Vector<Double> pixel2 = pixel.copy(); auto csys = _getCoords(); { Vector<Double> replace = csys.referencePixel(); const Int nIn = pixel2.size(); const Int nOut = replace.size(); Vector<Double> out(nOut); for (Int i = 0; i < nOut; ++i) { if (i > nIn - 1) { out(i) = replace(i); } else { out(i) = pixel2(i); } } pixel2.assign(out); } // Convert to world Vector<Double> world; Record rec; String dFrame = dirFrame; dFrame.upcase(); String fFrame = freqFrame; fFrame.upcase(); MDirection::Types dirType = csys.hasDirectionCoordinate() ? csys.directionCoordinate().directionType(dFrame == "CL") : MDirection::J2000; MFrequency::Types freqType = csys.hasSpectralAxis() ? csys.spectralCoordinate().frequencySystem(fFrame == "CL") : MFrequency::LSRK; if ( (! csys.hasDirectionCoordinate() || dFrame == "CL") && (! csys.hasSpectralAxis() || fFrame == "CL") ) { ThrowIf( ! csys.toWorld(world, pixel2, true), "Error converting to world coordinates: " + csys.errorMessage() ); } else if ( (! csys.hasDirectionCoordinate() || dFrame == "NATIVE") && (! csys.hasSpectralAxis() || fFrame == "NATIVE") ) { ThrowIf( ! csys.toWorld(world, pixel2, false), "Error converting to world coordinates: " + csys.errorMessage() ); } else { if (csys.hasDirectionCoordinate() && dFrame != "CL") { if (dFrame == "NATIVE") { dirType = csys.directionCoordinate().directionType(false); } else { ThrowIf( ! MDirection::getType(dirType, dFrame), "Unknown direction reference frame " + dirFrame ); } auto dirCoord = csys.directionCoordinate(); dirCoord.setReferenceConversion(dirType); csys.replaceCoordinate(dirCoord, csys.directionCoordinateNumber()); } if (csys.hasSpectralAxis() && fFrame != "CL") { if (fFrame == "NATIVE") { freqType = csys.spectralCoordinate().frequencySystem(false); } else { ThrowIf( ! MFrequency::getType(freqType, fFrame), "Unknown frequency reference frame " + freqFrame ); } auto specCoord = csys.spectralCoordinate(); MFrequency::Types clFrame; MEpoch epoch; MPosition pos; MDirection dir; specCoord.getReferenceConversion(clFrame, epoch, pos, dir); specCoord.setReferenceConversion(freqType, epoch, pos, dir); csys.replaceCoordinate(specCoord, csys.spectralCoordinateNumber()); } ThrowIf( ! csys.toWorld(world, pixel2, true), "Error converting to world coordinates: " + csys.errorMessage() ); } return _worldVectorToRecord( csys, world, -1, format, true, true, doVelocity, dirType, freqType ); } template <class T> Record ImageMetaDataBase<T>::_worldVectorToRecord( const CoordinateSystem& csys, const Vector<Double>& world, Int c, const String& format, Bool isAbsolute, Bool showAsAbsolute, Bool doVelocity, MDirection::Types dirFrame, MFrequency::Types freqFrame ) { // World vector must be in the native units of cSys // c = -1 means world must be length cSys.nWorldAxes // c > 0 means world must be length cSys.coordinate(c).nWorldAxes() // format from 'n,q,s,m' auto ct = upcase(format); Vector<String> units; if (c < 0) { units = csys.worldAxisUnits(); } else { units = csys.coordinate(c).worldAxisUnits(); } AlwaysAssert(world.size() == units.size(),AipsError); Record rec; if (ct.contains("N")) { rec.define("numeric", world); } if (ct.contains("Q")) { String error; Record recQ1, recQ2; for (uInt i = 0; i < world.size(); ++i) { Quantum<Double> worldQ(world(i), Unit(units(i))); QuantumHolder h(worldQ); ThrowIf(! h.toRecord(error, recQ1), error); recQ2.defineRecord(i, recQ1); } rec.defineRecord("quantity", recQ2); } if (ct.contains("S")) { Vector<Int> worldAxes; if (c < 0) { worldAxes.resize(world.size()); indgen(worldAxes); } else { worldAxes = csys.worldAxes(c); } Coordinate::formatType fType = Coordinate::SCIENTIFIC; Int prec = 8; String u; Int coord, axisInCoord; Vector<String> fs(world.nelements()); for (uInt i = 0; i < world.size(); ++i) { csys.findWorldAxis(coord, axisInCoord, i); if ( csys.type(coord) == Coordinate::DIRECTION || csys.type(coord) == Coordinate::STOKES ) { fType = Coordinate::DEFAULT; } else { fType = Coordinate::SCIENTIFIC; } u = ""; fs(i) = csys.format( u, fType, world(i), worldAxes(i), isAbsolute, showAsAbsolute, prec ); if (! u.empty() && (u != " ")) { fs(i) += " " + u; } } rec.define("string", fs); } if (ct.contains(String("M"))) { Record recM = _worldVectorToMeasures( csys, world, c, isAbsolute, doVelocity, dirFrame, freqFrame ); rec.defineRecord("measure", recM); } return rec; } template <class T> Record ImageMetaDataBase<T>::_worldVectorToMeasures( const CoordinateSystem& csys, const Vector<Double>& world, Int c, Bool abs, Bool doVelocity, MDirection::Types dirFrame, MFrequency::Types freqFrame ) { LogIO log; log << LogOrigin("ImageMetaDataBase", __func__); uInt directionCount, spectralCount, linearCount, stokesCount, tabularCount; directionCount = spectralCount = linearCount = stokesCount = tabularCount = 0; // Loop over desired Coordinates Record rec; String error; uInt s, e; if (c < 0) { AlwaysAssert(world.nelements()==csys.nWorldAxes(), AipsError); s = 0; e = csys.nCoordinates(); } else { AlwaysAssert(world.nelements()==csys.coordinate(c).nWorldAxes(), AipsError); s = c; e = c + 1; } for (uInt i = s; i < e; ++i) { // Find the world axes in the CoordinateSystem that this coordinate belongs to const auto& worldAxes = csys.worldAxes(i); const auto nWorldAxes = worldAxes.size(); Vector<Double> world2(nWorldAxes); const auto& coord = csys.coordinate(i); auto units = coord.worldAxisUnits(); Bool none = true; // Fill in missing world axes if all coordinates specified if (c < 0) { for (uInt j = 0; j < nWorldAxes; ++j) { if (worldAxes(j) < 0) { world2[j] = coord.referenceValue()[j]; } else { world2(j) = world(worldAxes[j]); none = false; } } } else { world2 = world; none = false; } if ( csys.type(i) == Coordinate::LINEAR || csys.type(i) == Coordinate::TABULAR ) { if (!none) { Record linRec1, linRec2; for (uInt k = 0; k < world2.size(); ++k) { Quantum<Double> value(world2(k), units(k)); QuantumHolder h(value); ThrowIf( ! h.toRecord(error, linRec1), error ); linRec2.defineRecord(k, linRec1); } if (csys.type(i) == Coordinate::LINEAR) { rec.defineRecord("linear", linRec2); } else if (csys.type(i) == Coordinate::TABULAR) { rec.defineRecord("tabular", linRec2); } } if (csys.type(i) == Coordinate::LINEAR) { ++linearCount; } if (csys.type(i) == Coordinate::TABULAR) { ++tabularCount; } } else if (csys.type(i) == Coordinate::DIRECTION) { ThrowIf( ! abs, "It is not possible to have a relative MDirection measure" ); AlwaysAssert(worldAxes.nelements() == 2,AipsError); if (!none) { // Make an MDirection and stick in record Quantum<Double> t1(world2(0), units(0)); Quantum<Double> t2(world2(1), units(1)); MDirection direction( t1, t2, dirFrame ); MeasureHolder h(direction); Record dirRec; ThrowIf( ! h.toRecord(error, dirRec), error ); rec.defineRecord("direction", dirRec); } directionCount++; } else if (csys.type(i) == Coordinate::SPECTRAL) { ThrowIf( ! abs, "It is not possible to have a relative MFrequency measure" ); AlwaysAssert(worldAxes.nelements()==1,AipsError); if (!none) { // Make an MFrequency and stick in record Record specRec, specRec1; Quantum<Double> t1(world2(0), units(0)); const auto& sc0 = csys.spectralCoordinate(i); MFrequency frequency(t1, freqFrame); MeasureHolder h(frequency); ThrowIf( ! h.toRecord(error, specRec1), error ); specRec.defineRecord("frequency", specRec1); if (doVelocity) { SpectralCoordinate sc(sc0); // Do velocity conversions and stick in MDOppler // Radio sc.setVelocity(String("km/s"), MDoppler::RADIO); Quantum<Double> velocity; ThrowIf( ! sc.frequencyToVelocity(velocity, frequency), sc.errorMessage() ); MDoppler v(velocity, MDoppler::RADIO); MeasureHolder h(v); ThrowIf( ! h.toRecord(error, specRec1), error ); specRec.defineRecord("radiovelocity", specRec1); // Optical sc.setVelocity(String("km/s"), MDoppler::OPTICAL); ThrowIf( ! sc.frequencyToVelocity(velocity, frequency), sc.errorMessage() ); v = MDoppler(velocity, MDoppler::OPTICAL); h = MeasureHolder(v); ThrowIf( ! h.toRecord(error, specRec1), error ); specRec.defineRecord("opticalvelocity", specRec1); // beta (relativistic/true) sc.setVelocity(String("km/s"), MDoppler::BETA); ThrowIf( ! sc.frequencyToVelocity(velocity, frequency), sc.errorMessage() ); v = MDoppler(velocity, MDoppler::BETA); h = MeasureHolder(v); ThrowIf( ! h.toRecord(error, specRec1), error ); specRec.defineRecord("betavelocity", specRec1); } rec.defineRecord("spectral", specRec); } ++spectralCount; } else if (csys.type(i) == Coordinate::STOKES) { ThrowIf ( ! abs, "It makes no sense to have a relative Stokes measure" ); AlwaysAssert(worldAxes.size() == 1, AipsError); if (!none) { const auto& coord0 = csys.stokesCoordinate(i); StokesCoordinate coord(coord0); // non-const String u; auto s = coord.format( u, Coordinate::DEFAULT, world2(0), 0, true, true, -1 ); rec.define("stokes", s); } ++stokesCount; } else { ThrowCc("Cannot handle Coordinates of type " + csys.showType(i)); } } if (directionCount > 1) { log << LogIO::WARN << "There was more than one DirectionCoordinate in the " << LogIO::POST; log << LogIO::WARN << "CoordinateSystem. Only the last one is returned" << LogIO::POST; } if (spectralCount > 1) { log << LogIO::WARN << "There was more than one SpectralCoordinate in the " << LogIO::POST; log << LogIO::WARN << "CoordinateSystem. Only the last one is returned" << LogIO::POST; } if (stokesCount > 1) { log << LogIO::WARN << "There was more than one StokesCoordinate in the " << LogIO::POST; log << LogIO::WARN << "CoordinateSystem. Only the last one is returned" << LogIO::POST; } if (linearCount > 1) { log << LogIO::WARN << "There was more than one LinearCoordinate in the " << LogIO::POST; log << LogIO::WARN << "CoordinateSystem. Only the last one is returned" << LogIO::POST; } if (tabularCount > 1) { log << LogIO::WARN << "There was more than one TabularCoordinate in the " << LogIO::POST; log << LogIO::WARN << "CoordinateSystem. Only the last one is returned" << LogIO::POST; } return rec; } } #endif