//# 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 IMAGES_IMAGEMETADATARW_TCC #define IMAGES_IMAGEMETADATARW_TCC #include <imageanalysis/ImageAnalysis/ImageMetaDataRW.h> #include <casacore/casa/Containers/ValueHolder.h> #include <casacore/casa/Quanta/QuantumHolder.h> #include <casacore/coordinates/Coordinates/CoordinateUtil.h> #include <casacore/coordinates/Coordinates/DirectionCoordinate.h> #include <casacore/coordinates/Coordinates/SpectralCoordinate.h> #include <casacore/tables/Tables/TableRecord.h> #include <imageanalysis/ImageAnalysis/ImageHistory.h> #include <casacore/casa/aips.h> #include <iomanip> #define _LOCATEA "ImageMetaDataRW" << __func__ << " " #define _ORIGINA LogOrigin("ImageMetaDataRW", __func__) using namespace casacore; namespace casa { template <class T> ImageMetaDataRW<T>::ImageMetaDataRW(SPIIT image) : ImageMetaDataBase<T>(image), _image(image) {} template <class T> Record ImageMetaDataRW<T>::toRecord(Bool verbose) const { if (_header.empty()) { _header = this->_makeHeader(); } if (verbose) { this->_toLog(_header); } return _header; } template <class T> Bool ImageMetaDataRW<T>::_isWritable() const { return _image->isWritable(); } template <class T> Bool ImageMetaDataRW<T>::_setUnit(const String& unit) { return _image->setUnits(unit); } template <class T> const TableRecord ImageMetaDataRW<T>::_miscInfo() const { return _image->miscInfo(); } template <class T> void ImageMetaDataRW<T>::_setMiscInfo(const TableRecord& rec) { auto res = _image->setMiscInfo(rec); ThrowIf( ! res, "Error setting misc info" ); } template <class T> Bool ImageMetaDataRW<T>::add(const String& key, const ValueHolder& value) { ThrowIf( ! _isWritable(), "This image is not writable; metadata may not be added to it" ); String c = key; c.downcase(); ThrowIf( c.startsWith(ImageMetaDataConstants::_CDELT) || c.startsWith(ImageMetaDataConstants::_CRPIX) || c.startsWith(ImageMetaDataConstants::_CRVAL) || c.startsWith(ImageMetaDataConstants::_CTYPE) || c.startsWith(ImageMetaDataConstants::_CUNIT), key + " pertains to a " + "coordinate system axis attribute. It may be " + "modified if it exists, but it may not be added." ); ThrowIf( c == ImageMetaDataConstants::_EQUINOX, "The direction reference frame (" + key + "=" + _getEquinox() +") already exists. It may be modified but not added." ); ThrowIf( c == ImageMetaDataConstants::MASKS, "This application does not support adding masks." ); ThrowIf( c == ImageMetaDataConstants::_OBSDATE || c == ImageMetaDataConstants::_EPOCH, "The epoch (" + key + "=" + this->_getEpochString() + ") already exists. It may be modified but not added." ); ThrowIf( c == ImageMetaDataConstants::_PROJECTION, "The projection (" + key + "=" + _getProjection() +") already exists. It may be modified but not added." ); ThrowIf( c == ImageMetaDataConstants::_REFFREQTYPE, "The velocity reference frame (" + key + "=" + _getProjection() +") already exists. It may be modified but not added." ); ThrowIf( c == ImageMetaDataConstants::_SHAPE, "The shape is intrinsic to the image and may " "not be added." ); ThrowIf( c == ImageMetaDataConstants::_BEAMPA || c == ImageMetaDataConstants::_BPA, "Cannot add a beam position " "angle. Add the major or minor axis and then " "modify the other and the position angle." ); ThrowIf( c == ImageMetaDataConstants::_DATAMIN || c == ImageMetaDataConstants::_DATAMAX || c == ImageMetaDataConstants::_MINPIXPOS || c == ImageMetaDataConstants::_MAXPIXPOS || c == ImageMetaDataConstants::_MINPOS || c == ImageMetaDataConstants::_MAXPOS, key + " is is a statistic of the image and may " "not be added." ); if (c == ImageMetaDataConstants::_BUNIT) { ThrowIf( ! _getBrightnessUnit().empty(), key + " is already present and has value " + _getBrightnessUnit() + ". It may be modified but not added." ); String v = value.asString(); ThrowIf( ! _setUnit(v), "Unable to set " + key ); _bunit = v; _modHistory(__func__, c, String(""), v); } else if (c == ImageMetaDataConstants::_IMTYPE) { String imtype = _getImType(); ThrowIf( ! imtype.empty(), "The image type (" + key + "=" + ImageInfo::imageType(imtype) + ") already exists. It may be modified but be added." ); set(c, value); } else if (c == ImageMetaDataConstants::_OBJECT) { String object = _getObject(); ThrowIf( ! object.empty(), key + " is already present and has value " + object + ". It may be modified but not added." ); set(c, value); } else if (c == ImageMetaDataConstants::_OBSERVER) { String observer = _getObserver(); ThrowIf( ! observer.empty(), key + " is already present and has value " + observer + ". It may be modified but not added." ); set(c, value); } else if (c == ImageMetaDataConstants::_RESTFREQ) { ThrowIf( _getRestFrequency().getValue() > 0, "The rest frequency (" + key + "=" + String::toString(_getRestFrequency().getValue()) + _getRestFrequency().getUnit() + ") already exists. It may be modified but not added " + "by this application. If you wish to append a rest frequency " + "to an already existing list, use cs.setrestfrequency()." ); set(c, value); } else if (c == ImageMetaDataConstants::_TELESCOPE) { String telescope = _getTelescope(); ThrowIf( ! telescope.empty(), key + " is already present and has value " + telescope + ". It may be modified but not added." ); set(c, value); } else if ( c == ImageMetaDataConstants::_BEAMMAJOR || c == ImageMetaDataConstants::_BEAMMINOR || c == ImageMetaDataConstants::_BMAJ || c == ImageMetaDataConstants::_BMIN ) { ImageInfo info = _getInfo(); ThrowIf( info.hasBeam(), "This image already has a beam(s). Cannot add one." ); set(c, value); } else if ( _miscInfo().isDefined(key) || _miscInfo().isDefined(c) ) { ThrowCc( "Keyword " + key + " already exists so cannot be added." ); } else { _setUserDefined(key, value); } // clear cached header _header.assign(Record()); return true; } template <class T> Bool ImageMetaDataRW<T>::remove(const String& key) { ThrowIf( ! _isWritable(), "This image is not writable; metadata may not be removed from it" ); String c = key; auto log = this->_getLog(); log << _ORIGINA; c.downcase(); ThrowIf( c.startsWith(ImageMetaDataConstants::_CDELT) || c.startsWith(ImageMetaDataConstants::_CRPIX) || c.startsWith(ImageMetaDataConstants::_CRVAL) || c.startsWith(ImageMetaDataConstants::_CTYPE) || c.startsWith(ImageMetaDataConstants::_CUNIT), key + " pertains to a " "coordinate system axis attribute. It may be " "modified, but it may not be removed." ); ThrowIf( c == ImageMetaDataConstants::_EQUINOX, "Although the direction reference frame (" + key + ") may be modified, it may not be removed." ); ThrowIf( c == ImageMetaDataConstants::_IMTYPE, "Although the image type (" + key + ") may be modified, it may not be removed." ); ThrowIf( c == ImageMetaDataConstants::MASKS, "Logic Error: removeMask() should be called instead" ); ThrowIf( c == ImageMetaDataConstants::_OBSDATE || c == ImageMetaDataConstants::_EPOCH, "Although the epoch (" + key + ") may be modified, it cannot be removed." ); ThrowIf( c == ImageMetaDataConstants::_PROJECTION, "Although the projection (" + key + ") may be modified, it cannot be removed." ); ThrowIf( c == ImageMetaDataConstants::_REFFREQTYPE, "Although the velocity reference frame (" + key + ") may be modified, it cannot be removed." ); ThrowIf( c == ImageMetaDataConstants::_RESTFREQ, "Although the rest frequency (" + key + ") may be modified, it cannot be removed." ); ThrowIf( c == ImageMetaDataConstants::_SHAPE, "The shape is intrinsic to the image and may " "not be modified nor removed." ); ThrowIf( c == ImageMetaDataConstants::_DATAMIN || c == ImageMetaDataConstants::_DATAMAX || c == ImageMetaDataConstants::_MINPIXPOS || c == ImageMetaDataConstants::_MAXPIXPOS || c == ImageMetaDataConstants::_MINPOS || c == ImageMetaDataConstants::_MAXPOS, key + " is is a statistic of the image and may " + "not be modified nor removed by this application." ); if (c == ImageMetaDataConstants::_BUNIT) { auto oldUnit = _getBrightnessUnit(); ThrowIf( ! _setUnit(""), "Unable to clear " + key ); _bunit = ""; _modHistory(__func__, c, oldUnit, String("")); log << LogIO::NORMAL << "Setting " << key << " to empty string" << LogIO::POST; } else if (c == ImageMetaDataConstants::_OBJECT) { ImageInfo info = _getInfo(); auto old = info.objectName(); info.setObjectName(""); _setImageInfo(info); log << LogIO::NORMAL << "Setting " << key << " to empty string" << LogIO::POST; _object = ""; _modHistory(__func__, c, old, String("")); } else if (c == ImageMetaDataConstants::_OBSERVER) { CoordinateSystem csys = _getCoords(); ObsInfo info = csys.obsInfo(); auto old = info.observer(); info.setObserver(""); csys.setObsInfo(info); _setCsys(csys); log << LogIO::NORMAL << "Setting " << key << " to empty string" << LogIO::POST; _observer = ""; _modHistory(__func__, c, old, String("")); } else if (c == ImageMetaDataConstants::_TELESCOPE) { CoordinateSystem csys = _getCoords(); ObsInfo info = csys.obsInfo(); auto old = info.telescope(); info.setTelescope(""); csys.setObsInfo(info); _setCsys(csys); log << LogIO::NORMAL << "Setting " << key << " to empty string" << LogIO::POST; _telescope = ""; _modHistory(__func__, c, old, String("")); } else if ( c == ImageMetaDataConstants::_BEAMMAJOR || c == ImageMetaDataConstants::_BEAMMINOR || c == ImageMetaDataConstants::_BEAMPA || c == ImageMetaDataConstants::_BMAJ || c == ImageMetaDataConstants::_BMIN || c == ImageMetaDataConstants::_BPA ) { ImageInfo info = _getInfo(); if (info.hasBeam()) { String history; if (info.hasSingleBeam()) { log << LogIO::NORMAL << "Removing this image's single beam" << LogIO::POST; history = "Removed image's single beam"; } else { log << LogIO::NORMAL << "Removing all of this image's multiple beams" << LogIO::POST; history = "Removed image's multiple beams"; } info.removeRestoringBeam(); _setImageInfo(info); _beam = GaussianBeam::NULL_BEAM; _toHistory(__func__, history); } else { log << LogIO::WARN << "This image has no beam(s) to remove." << LogIO::POST; return false; } } else if (_miscInfo().isDefined(key)) { TableRecord info = _miscInfo(); info.removeField(key); _setMiscInfo(info); auto history = "Removed user-defined keyword " + key; log << LogIO::NORMAL << history << LogIO::POST; _toHistory(__func__, history); } else if (_miscInfo().isDefined(c)) { TableRecord info = _miscInfo(); info.removeField(c); _setMiscInfo(info); auto history = "Removed user-defined keyword " + c; log << LogIO::NORMAL << history << LogIO::POST; _toHistory(__func__, history); } else { ThrowCc("Unknown keyword " + c); } _header.assign(Record()); return true; } template <class T> Bool ImageMetaDataRW<T>::_hasRegion(const String& maskName) const { return _image->hasRegion(maskName, RegionHandler::Masks); } template <class T> Bool ImageMetaDataRW<T>::removeMask(const String& maskName) { ThrowIf( ! _isWritable(), "This image is not writable; a mask(s) may not be removed from it" ); auto log = this->_getLog(); log << _ORIGINA; if (maskName.empty()) { Vector<String> masks = _getMasks().copy(); if (masks.size() == 0) { log << LogIO::WARN << "This image has no masks, so nothing to do." << LogIO::POST; return true; } else { Vector<String>::const_iterator end = masks.end(); for ( Vector<String>::const_iterator iter=masks.begin(); iter!=end; iter++ ) { removeMask(*iter); } _masks.resize(0); return _getMasks().size() == 0; } } else { ThrowIf( ! _hasRegion(maskName), "No mask named " + maskName + " found" ); _image->removeRegion(maskName, RegionHandler::Masks); ThrowIf( _hasRegion(maskName), "Unable to remove mask " + maskName ); _masks.resize(0); log << LogIO::NORMAL << "Removed mask named " << maskName << endl; _header.assign(Record()); return true; } } template <class T> void ImageMetaDataRW<T>::setCsys(const Record& coordinates) { ThrowIf( coordinates.nfields() == 0, "Record is empty" ); ThrowIf( ! _setCsys( *_makeCoordinateSystem( coordinates, this->_getShape() ) ), "Unable to set coordinate system" ); } template <class T> std::unique_ptr<CoordinateSystem> ImageMetaDataRW<T>::_makeCoordinateSystem( const Record& coordinates, const IPosition& shape ) { std::unique_ptr<CoordinateSystem> pCS; if (coordinates.nfields() == 1) { // must be a record as an element Record tmp(coordinates.asRecord(RecordFieldId(0))); pCS.reset(CoordinateSystem::restore(tmp, "")); } else { pCS.reset(CoordinateSystem::restore(coordinates, "")); } // Fix up any body longitude ranges... String errMsg; if (! CoordinateUtil::cylindricalFix(*pCS, errMsg, shape)) { this->_getLog() << LogOrigin("ImageMetaDataRW", __func__) << LogIO::WARN << errMsg << LogIO::POST; } return pCS; } template <class T> Bool ImageMetaDataRW<T>::_setCsys(const CoordinateSystem& csys) { return _image->setCoordinateInfo(csys); } template <class T> Bool ImageMetaDataRW<T>::_setImageInfo(const ImageInfo& info) { return _image->setImageInfo(info); } template <class T> Quantity ImageMetaDataRW<T>::_getQuantity(const ValueHolder& v) { QuantumHolder qh; String error; DataType type = v.dataType(); if (type == TpRecord) { ThrowIf( ! qh.fromRecord(error, v.asRecord()), "Error converting to Quantity. " + error ); return qh.asQuantity(); } else if (type == TpString) { Quantity q; ThrowIf( ! readQuantity(q, v.asString()), "Error converting " + v.asString() + " to Quantity" ); return q; } else { ostringstream os; os << "Input ValueHolder is of type " << v.dataType() << ", but it must be either " << " a Record or a String"; ThrowCc(os.str()); } } template <class T> Bool ImageMetaDataRW<T>::set( const String& key, const ValueHolder& value ) { ThrowIf( ! _isWritable(), "This image is not writable; metadata may not be modified in it" ); String c = key; c.downcase(); ValueHolder old; if (c == ImageMetaDataConstants::_BUNIT) { auto v = _getString(key, value); old = ValueHolder(_getBrightnessUnit()); if (_setUnit(v)) { _bunit = v; } else { ThrowCc("Unable to set " + key); } } else if ( c.startsWith(ImageMetaDataConstants::_CDELT) || c.startsWith(ImageMetaDataConstants::_CRPIX) || c.startsWith(ImageMetaDataConstants::_CRVAL) || c.startsWith(ImageMetaDataConstants::_CTYPE) || c.startsWith(ImageMetaDataConstants::_CUNIT) ) { _setCoordinateValue(c, value); } else if (c == ImageMetaDataConstants::_EQUINOX) { ThrowIf( ! _getCoords().hasDirectionCoordinate(), "This image does not have a direction " "coordinate and so a direction projection cannot be added." ); String v = _getString(key, value); v.upcase(); MDirection::Types type; ThrowIf( !MDirection::getType(type, v), "Unknown direction reference frame specification" ); CoordinateSystem csys = _getCoords(); DirectionCoordinate dircoord = csys.directionCoordinate(); if (dircoord.directionType(false) == type) { // nothing to do return true; } old = ValueHolder( MDirection::showType(dircoord.directionType(false)) ); dircoord.setReferenceFrame(type); csys.replaceCoordinate(dircoord, csys.directionCoordinateNumber()); _setCsys(csys); _equinox = v; } else if (c == ImageMetaDataConstants::_IMTYPE) { String v = _getString(key, value); String imtype = _getImType(); /* ImageInfo info = _floatImage ? _floatImage->imageInfo() : _complexImage->imageInfo(); */ auto info = _image->imageInfo(); old = ValueHolder(ImageInfo::imageType(info.imageType())); info.setImageType(ImageInfo::imageType(v)); _setImageInfo(info); String newType = ImageInfo::imageType(info.imageType()); _imtype = newType; } else if (c == ImageMetaDataConstants::MASKS) { ThrowCc("This application does not support modifying masks."); } else if (c == ImageMetaDataConstants::_OBJECT) { String v = _getString(key, value); String object = _getObject(); ImageInfo info = _getInfo(); old = ValueHolder(info.objectName()); info.setObjectName(v); _setImageInfo(info); _object = v; } else if (c == ImageMetaDataConstants::_OBSDATE || c == ImageMetaDataConstants::_EPOCH) { ThrowIf( value.dataType() == TpString && value.asString().empty(), key + " value not specified" ); Quantity qval = _getQuantity(value); ThrowIf( ! qval.isConform("s"), key + " value must have units of time or be in a supported time format" ); MEpoch epoch(qval); CoordinateSystem csys = _getCoords(); ObsInfo info = csys.obsInfo(); auto oldEpoch = info.obsDate(); ostringstream oss; oss << oldEpoch << endl; old = ValueHolder(oss.str()); info.setObsDate(epoch); csys.setObsInfo(info); _setCsys(csys); _obsdate = epoch; } else if (c == ImageMetaDataConstants::_OBSERVER) { String v = _getString(key, value); CoordinateSystem csys = _getCoords(); ObsInfo info = csys.obsInfo(); old = ValueHolder(info.observer()); info.setObserver(v); csys.setObsInfo(info); _setCsys(csys); _observer = v; } else if (c == ImageMetaDataConstants::_PROJECTION) { String v = _getString(key, value); ThrowIf( ! _getCoords().hasDirectionCoordinate(), "This image does not have a direction " "coordinate and so a direction projection cannot be added." ); v.upcase(); Projection::Type ptype = Projection::type(v); ThrowIf( ptype == Projection::N_PROJ, "Unknown projection specification " + v ); CoordinateSystem csys = _getCoords(); DirectionCoordinate dircoord = csys.directionCoordinate(); Projection curProj = dircoord.projection(); if (curProj.type() == ptype) { // nothing to do return true; } Vector<Double> curParms = curProj.parameters(); Projection projection(ptype, curParms); old = ValueHolder(dircoord.projection().name()); dircoord.setProjection(projection); csys.replaceCoordinate(dircoord, csys.directionCoordinateNumber()); _setCsys(csys); _projection = _getProjection(); } else if (c == ImageMetaDataConstants::_REFFREQTYPE) { String v = _getString(key, value); ThrowIf( ! _getCoords().hasSpectralAxis(), "This image does not have a spectral coordinate" "and so a velocity reference frame cannot be added." ); v.upcase(); MFrequency::Types type; ThrowIf( ! MFrequency::getType(type, v), "Unknown velocity reference frame specification " + v ); CoordinateSystem csys = _getCoords(); SpectralCoordinate spcoord = csys.spectralCoordinate(); if (spcoord.frequencySystem(false) == type) { return true; } old = ValueHolder(MFrequency::showType(spcoord.frequencySystem(false))); spcoord.setFrequencySystem(type); csys.replaceCoordinate(spcoord, csys.spectralCoordinateNumber()); _setCsys(csys); _reffreqtype = v; } else if (c == ImageMetaDataConstants::_RESTFREQ) { ThrowIf( ! _getCoords().hasSpectralAxis(), "This image does not have a spectral coordinate" "and so a velocity reference frame cannot be added." ); Quantity rf = _getQuantity(value); ThrowIf( rf.getValue() <= 0, "Unable to set rest frequency to " + value.asString() ); ThrowIf( ! rf.getUnit().empty() && ! rf.isConform("Hz"), "Unable to set rest frequency to " + value.asString() + " because units do not conform to Hz" ); CoordinateSystem csys = _getCoords(); SpectralCoordinate sp = csys.spectralCoordinate(); DataType type = value.dataType(); Double v = ( type == TpShort || type == TpUShort || type == TpInt || type == TpUInt || type == TpFloat || type == TpDouble ) ? value.asDouble() : rf.getValue(sp.worldAxisUnits()[0]); old = ValueHolder(sp.restFrequency()); sp.setRestFrequency(v); csys.replaceCoordinate(sp, csys.spectralCoordinateNumber()); _setCsys(csys); _restFreq = rf; } else if (c == ImageMetaDataConstants::_SHAPE) { ThrowCc( "The shape is intrinsic to the image and may " "not be modified." ); } else if (c == ImageMetaDataConstants::_TELESCOPE) { String v = _getString(key, value); CoordinateSystem csys = _getCoords(); ObsInfo info = csys.obsInfo(); old = ValueHolder(info.telescope()); info.setTelescope(v); csys.setObsInfo(info); _setCsys(csys); _telescope = v; } else if ( c == ImageMetaDataConstants::_BEAMMAJOR || c == ImageMetaDataConstants::_BEAMMINOR || c == ImageMetaDataConstants::_BEAMPA || c == ImageMetaDataConstants::_BMAJ || c == ImageMetaDataConstants::_BMIN || c == ImageMetaDataConstants::_BPA ) { ImageInfo info = _getInfo(); ThrowIf( info.hasMultipleBeams(), "This image has multiple beams. " "This application cannot modify beams in such an image." ) Quantity v = _getQuantity(value); GaussianBeam beam; ostringstream oss; if (c == ImageMetaDataConstants::_BEAMPA || c == ImageMetaDataConstants::_BPA) { ThrowIf( ! info.hasBeam(), "This image has no beam. This application cannot add a beam position " "angle to an image with no beam. Add the major or minor axis and then " "modify the other and the position angle with put." ); beam = info.getBeamSet()(0, 0); oss << beam.getPA(false); old = ValueHolder(oss.str()); beam.setPA(v); } else if (info.hasBeam()) { beam = info.getBeamSet()(0, 0); if ( c == ImageMetaDataConstants::_BEAMMAJOR || c == ImageMetaDataConstants::_BMAJ ) { oss << beam.getMajor() << endl; beam.setMajorMinor(v, beam.getMinor()); } else { oss << beam.getMinor() << endl; beam.setMajorMinor(beam.getMajor(), v); } old = ValueHolder(oss.str()); } else { beam = GaussianBeam(v, v, Quantity(0, "deg")); oss << "Added beam " << beam << endl; _toHistory(__func__, oss.str()); } info.setRestoringBeam(beam); _setImageInfo(info); _beam = beam; this->_getLog() << LogIO::NORMAL << "Updated single beam " << beam << " in image." << LogIO::POST; } else if ( c == ImageMetaDataConstants::_DATAMIN || c == ImageMetaDataConstants::_DATAMAX || c == ImageMetaDataConstants::_MINPIXPOS || c == ImageMetaDataConstants::_MAXPIXPOS || c == ImageMetaDataConstants::_MINPOS || c == ImageMetaDataConstants::_MAXPOS ) { ThrowCc( key + " is is a statistic of the image and may " "not be added or modified." ); } else { _setUserDefined(key, value); } if (! old.isNull()) { _modHistory(__func__, key, old, value); } // clear the cached header values _header.assign(Record()); return true; } template <class T> void ImageMetaDataRW<T>::_setUserDefined( const String& key, const ValueHolder& value ) { TableRecord info = _miscInfo(); DataType type = value.dataType(); switch(type) { case TpBool: info.define(key, value.asBool()); break; case TpArrayBool: info.define(key, value.asArrayBool()); break; case TpComplex: info.define(key, value.asComplex()); break; case TpDouble: info.define(key, value.asDouble()); break; case TpArrayDouble: info.define(key, value.asArrayDouble()); break; case TpInt: info.define(key, value.asInt()); break; case TpArrayInt: info.define(key, value.asArrayInt()); break; case TpInt64: info.define(key, value.asInt64()); break; case TpArrayInt64: info.define(key, value.asArrayInt64()); break; case TpRecord: { info.defineRecord(key, value.asRecord()); break; } case TpString: info.define(key, value.asString()); break; case TpArrayString: { info.define(key, value.asArrayString()); break; } default: ostringstream os; os << "Unhandled value type " << type << " for " << key; ThrowCc(os.str()); break; } _setMiscInfo(info); _addHistory(__func__, key, value); } template <class T> void ImageMetaDataRW<T>::_setCoordinateValue( const String& key, const ValueHolder& value ) { LogIO log = this->_getLog(); String prefix = key.substr(0, 5); CoordinateSystem csys = _getCoords(); uInt n = this->_getAxisNumber(key); ValueHolder old; Bool isStokes = csys.hasPolarizationCoordinate() && (Int)n == csys.polarizationAxisNumber(false) + 1; if (prefix == ImageMetaDataConstants::_CDELT) { ThrowIf( isStokes, "A polarization axis cannot have an increment" ); Quantity qinc = _getQuantity(value); Vector<Double> increments = csys.increment(); auto oldUnit = _getAxisUnits()[n-1]; if (qinc.getFullUnit().empty()) { qinc.setUnit(oldUnit); } old = ValueHolder( String::toString(increments[n-1]) + oldUnit ); increments[n-1] = qinc.getValue(_getAxisUnits()[n-1]); csys.setIncrement(increments); if (! _increment.empty()) { _increment[n-1] = qinc; } } else if (prefix == ImageMetaDataConstants::_CRPIX) { ThrowIf( isStokes, "A polarization axis cannot have a reference pixel" ); DataType t = value.dataType(); Double x = 0; if (t == TpString) { x = String::toDouble(value.asString(), true); } else if ( t == TpInt || t == TpInt64 || t == TpDouble || t == TpFloat ) { x = value.asDouble(); } else { ostringstream os; os << t; ThrowCc("For crpix, value must be numeric, not " + os.str()); } auto refpix = _getRefPixel(); old = ValueHolder(refpix[n-1]); refpix[n-1] = x; csys.setReferencePixel(refpix); if (! _refPixel.empty()) { _refPixel[n-1] = refpix[n-1]; } } else if (prefix == ImageMetaDataConstants::_CRVAL) { DataType dType = value.dataType(); ThrowIf( dType == TpString && value.asString().empty(), key + " value not specified" ); Vector<Double> refval = csys.referenceValue(); if (isStokes) { ThrowIf( dType != TpString && dType != TpArrayString, "Data type to put must be either a string array or string" ); uInt nStokes = csys.stokesCoordinate().stokes().size(); Vector<String> stokesTypes; if (dType == TpString) { ThrowIf( nStokes > 1, "There are " + String::toString(nStokes) + " polarization values, " "so a string array of that length is needed" ); stokesTypes = Vector<String>(1, value.asString()); } else if (dType == TpArrayString) { ThrowIf( value.asArrayString().size() != nStokes, "There are " + String::toString(nStokes) + " polarization values, " "so a string array of that length is needed" ); stokesTypes = value.asArrayString(); } Vector<Int> stokesNumbers(nStokes); Vector<String>::const_iterator begin = stokesTypes.begin(); Vector<String>::const_iterator typeIter = stokesTypes.begin(); Vector<String>::const_iterator end = stokesTypes.end(); Vector<Int>::iterator numberIter = stokesNumbers.begin(); while (typeIter != end) { *numberIter = (Int)Stokes::type(*typeIter); typeIter++; numberIter++; } StokesCoordinate coord(stokesNumbers); old = ValueHolder(csys.stokesCoordinate().stokesStrings()); ThrowIf( ! csys.replaceCoordinate(coord, csys.polarizationCoordinateNumber()), "Failed to replace stokes coordinate" ); _stokes = stokesTypes; } else { auto qval = _getQuantity(value); auto oldUnit = _getAxisUnits()[n-1]; if (qval.getUnit().empty()) { qval.setUnit(oldUnit); } old = ValueHolder(String::toString(refval[n-1]) + oldUnit); refval[n-1] = qval.getValue(_getAxisUnits()[n-1]); csys.setReferenceValue(refval); if (! _refVal.empty()) { _refVal[n-1] = qval; } } } else if (prefix == ImageMetaDataConstants::_CTYPE) { auto names = _getAxisNames(); old = ValueHolder(names[n-1]); names[n-1] = _getString(key, value); csys.setWorldAxisNames(names); if (! _axisNames.empty()) { _axisNames[n-1] = names[n-1]; } } else if (prefix == ImageMetaDataConstants::_CUNIT) { ThrowIf( isStokes, "A polarization axis cannot have a unit" ); auto u = _getString(key, value); // Test to see if CASA supports this string as a Unit Unit x = Unit(u); Vector<String> units = _getAxisUnits(); old = ValueHolder(units[n-1]); units[n-1] = u; csys.setWorldAxisUnits(units, true); if (! _axisUnits.empty()) { _axisUnits[n-1] = units[n-1]; } } _setCsys(csys); _modHistory(__func__, key, old, value); // clear stats because modifying the coordinate system may invalidate // min and max value world coordinates _stats = Record(); } template <class T> String ImageMetaDataRW<T>::_getString( const String& key, const ValueHolder& v ) const { ThrowIf( v.dataType() != TpString, key + "value must be a string" ); return v.asString(); } template <class T> Vector<String> ImageMetaDataRW<T>::_getAxisNames() const { if (_axisNames.size() == 0) { _axisNames = _getCoords().worldAxisNames(); } return _axisNames; } template <class T> Vector<String> ImageMetaDataRW<T>::_getAxisUnits() const { if (_axisUnits.size() == 0) { _axisUnits = _getCoords().worldAxisUnits(); } return _axisUnits; } template <class T> GaussianBeam ImageMetaDataRW<T>::_getBeam() const { const ImageInfo& info = _getInfo(); if (info.hasSingleBeam()) { if (_beam == GaussianBeam::NULL_BEAM) { _beam = info.restoringBeam(-1, -1); } return _beam; } else if (info.hasMultipleBeams()) { throw AipsError("This image has multiple beams."); } else { throw AipsError("This image has no beam(s)."); } } template <class T> String ImageMetaDataRW<T>::_getBrightnessUnit() const { if (_bunit.empty()) { _bunit = _image->units().getName(); } return _bunit; } template <class T> String ImageMetaDataRW<T>::_getEquinox() const { const CoordinateSystem& csys = _getCoords(); if (_equinox.empty()) { if (csys.hasDirectionCoordinate()) { _equinox = MDirection::showType( csys.directionCoordinate().directionType() ); } } return _equinox; } template <class T> String ImageMetaDataRW<T>::_getImType() const { if (_imtype.empty()) { _imtype = ImageInfo::imageType(_getInfo().imageType()); } return _imtype; } template <class T> vector<Quantity> ImageMetaDataRW<T>::_getIncrements() const { if (_increment.size() == 0) { Vector<Double> incs = _getCoords().increment(); Vector<String> units = _getAxisUnits(); for (uInt i=0; i<incs.size(); i++) { _increment.push_back(Quantity(incs[i], units[i])); } } return _increment; } template <class T> String ImageMetaDataRW<T>::_getObject() const { if (_object.empty()) { _object = _getInfo().objectName(); } return _object; } template <class T> const ImageInfo& ImageMetaDataRW<T>::_getInfo() const { return _image->imageInfo(); } template <class T> const CoordinateSystem& ImageMetaDataRW<T>::_getCoords() const { return _image->coordinates(); } template <class T> Vector<String> ImageMetaDataRW<T>::_getMasks() const { if (_masks.empty()) { _masks = _image->regionNames(RegionHandler::Masks); } return _masks; } template <class T> MEpoch ImageMetaDataRW<T>::_getObsDate() const { if (_obsdate.get("s").getValue() == 0) { _obsdate = _getCoords().obsInfo().obsDate(); } return _obsdate; } template <class T> String ImageMetaDataRW<T>::_getObserver() const { if (_observer.empty()) { _observer = _getCoords().obsInfo().observer(); } return _observer; } template <class T> String ImageMetaDataRW<T>::_getProjection() const { if (_projection.empty()) { _projection = ImageMetaDataBase<T>::_getProjection(); } return _projection; } template <class T> Vector<Double> ImageMetaDataRW<T>::_getRefPixel() const { if (_refPixel.size() == 0) { _refPixel = _getCoords().referencePixel(); } return _refPixel; } template <class T> Vector<String> ImageMetaDataRW<T>::_getStokes() const { const CoordinateSystem csys = _getCoords(); ThrowIf( ! csys.hasPolarizationCoordinate(), "Logic Error: coordinate system does not have a polarization coordinate" ); if (_stokes.empty()) { _stokes = csys.stokesCoordinate().stokesStrings(); } return _stokes; } template <class T> Vector<Quantity> ImageMetaDataRW<T>::_getRefValue() const { if (_refVal.size() == 0) { Vector<Double> vals = _getCoords().referenceValue(); Vector<String> units = _getAxisUnits(); for (uInt i=0; i<vals.size(); i++) { _refVal.push_back(Quantity(vals[i], units[i])); } } return Vector<Quantity>(_refVal); } template <class T> String ImageMetaDataRW<T>::_getRefFreqType() const { const CoordinateSystem& csys = _getCoords(); if (_reffreqtype.empty() && csys.hasSpectralAxis()) { _reffreqtype = MFrequency::showType(csys.spectralCoordinate().frequencySystem(false)); } return _reffreqtype; } template <class T> Quantity ImageMetaDataRW<T>::_getRestFrequency() const { const CoordinateSystem& csys = _getCoords(); ThrowIf( ! csys.hasSpectralAxis(), "Image has no spectral axis so there is no rest frequency" ); if (_restFreq.getValue() == 0) { _restFreq = Quantity( csys.spectralCoordinate().restFrequency(), csys.spectralCoordinate().worldAxisUnits()[0] ); } return _restFreq; } template <class T> String ImageMetaDataRW<T>::_getTelescope() const { if (_telescope.empty()) { _telescope = _getCoords().obsInfo().telescope(); } return _telescope; } template <class T> Record ImageMetaDataRW<T>::_getStatistics() const { if (_stats.nfields() == 0 && isReal(_image->dataType())) { _stats = this->_calcStats(); } return _stats; } template <class T> template <class U> void ImageMetaDataRW<T>::_addHistory( const String& func, const String& keyword, const U& newVal ) { ostringstream oss; oss << "Added " << keyword << " = " << _quotify(newVal); _toHistory(func, oss.str()); } template <class T> template <class U, class V> void ImageMetaDataRW<T>::_modHistory( const String& func, const String& keyword, const U& oldVal, const V& newVal ) { ostringstream oss; oss << "Modified " << keyword << " from " << _quotify(oldVal) << " to " << _quotify(newVal) << endl; _toHistory(func, oss.str()); } template <class T> void ImageMetaDataRW<T>::_toHistory( const String& origin, const String& record ) { ImageHistory<T> ih(_image); ih.addHistory("ImageMetaDataRW::" + origin, record); } template <class T> template <class U> String ImageMetaDataRW<T>::_quotify(const U& val) { ostringstream oss; DataType x = whatType<U>(); if (x == TpOther && typeid(ValueHolder) == typeid(val)) { x = ((ValueHolder)val).dataType(); } if(x == TpString) { oss << "\"" << val << "\""; } else if (x == TpChar || x == TpUChar) { oss << "'" << val << "'"; } else if (x == TpFloat || x == TpComplex) { oss << std::setprecision(6) << val; } else if (x == TpDouble || x == TpDComplex) { oss << std::setprecision(12) << val; } else { oss << val; } return oss.str(); } } #endif