//# GaussianShape.cc: //# Copyright (C) 1998,1999,2000 //# 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 //# #include <imageanalysis/Annotations/AnnotationBase.h> #include <casacore/casa/Exceptions/Error.h> #include <casacore/casa/Quanta/MVAngle.h> #include <casacore/coordinates/Coordinates/DirectionCoordinate.h> #include <casacore/coordinates/Coordinates/SpectralCoordinate.h> #include <casacore/measures/Measures/MCDirection.h> #include <casacore/measures/Measures/VelocityMachine.h> #include <casacore/casa/Quanta/UnitMap.h> #include <iomanip> using namespace std; using namespace casacore; namespace casa { const AnnotationBase::RGB AnnotationBase::BLACK(3, 0.0); const AnnotationBase::RGB AnnotationBase::BLUE{0.0,0.0,255.0}; const AnnotationBase::RGB AnnotationBase::CYAN{255.0,255.0,0.0}; const AnnotationBase::RGB AnnotationBase::GRAY{190.0,190.0,190.0}; const AnnotationBase::RGB AnnotationBase::GREEN{0.0,255.0,0.0}; const AnnotationBase::RGB AnnotationBase::MAGENTA{255.0,0.0,255.0}; const AnnotationBase::RGB AnnotationBase::ORANGE{255.0,165.0,0.0}; const AnnotationBase::RGB AnnotationBase::RED{255.0,0.0,0.0}; const AnnotationBase::RGB AnnotationBase::WHITE(3, 255.0); const AnnotationBase::RGB AnnotationBase::YELLOW{255.0,255.0,0.0}; const String AnnotationBase::DEFAULT_LABEL = ""; const AnnotationBase::RGB AnnotationBase::DEFAULT_COLOR = AnnotationBase::GREEN; const AnnotationBase::LineStyle AnnotationBase::DEFAULT_LINESTYLE = AnnotationBase::SOLID; const uInt AnnotationBase::DEFAULT_LINEWIDTH = 1; const uInt AnnotationBase::DEFAULT_SYMBOLSIZE = 1; const uInt AnnotationBase::DEFAULT_SYMBOLTHICKNESS = 1; const String AnnotationBase::DEFAULT_FONT = "Helvetica"; const uInt AnnotationBase::DEFAULT_FONTSIZE = 10; const AnnotationBase::FontStyle AnnotationBase::DEFAULT_FONTSTYLE = AnnotationBase::BOLD; const Bool AnnotationBase::DEFAULT_USETEX = false; const AnnotationBase::RGB AnnotationBase::DEFAULT_LABELCOLOR = AnnotationBase::GREEN; const String AnnotationBase::DEFAULT_LABELPOS = "top"; const vector<Int> AnnotationBase::DEFAULT_LABELOFF = vector<Int>(2, 0); const String AnnotationBase::_class = "AnnotationBase"; std::list<string> AnnotationBase::_colorNames; Bool AnnotationBase::_doneUnitInit = false; Bool AnnotationBase::_doneColorInit = false; map<String, AnnotationBase::Type> AnnotationBase::_typeMap; map<String, AnnotationBase::LineStyle> AnnotationBase::_lineStyleMap; map<string, AnnotationBase::RGB> AnnotationBase::_colors; map<AnnotationBase::RGB, string> AnnotationBase::_rgbNameMap; const Regex AnnotationBase::rgbHexRegex("([0-9]|[a-f]){6}"); AnnotationBase::AnnotationBase( const Type type, const String& dirRefFrameString, const CoordinateSystem& csys, const Quantity& beginFreq, const Quantity& endFreq, const String& freqRefFrame, const String& dopplerString, const Quantity& restfreq, const Vector<Stokes::StokesTypes>& stokes ) : _type(type), _csys(csys), _label(DEFAULT_LABEL),_font(DEFAULT_FONT), _labelPos(DEFAULT_LABELPOS), _color(DEFAULT_COLOR), _labelColor(DEFAULT_LABELCOLOR), _fontstyle(DEFAULT_FONTSTYLE), _linestyle(DEFAULT_LINESTYLE), _fontsize(DEFAULT_FONTSIZE), _linewidth(DEFAULT_LINEWIDTH), _symbolsize(DEFAULT_SYMBOLSIZE), _symbolthickness(DEFAULT_SYMBOLTHICKNESS), _usetex(DEFAULT_USETEX), _convertedFreqLimits(0), _stokes(stokes), _globals(map<Keyword, Bool>()), _params(map<Keyword, String>()), _printGlobals(false), _labelOff(DEFAULT_LABELOFF) { ThrowIf( ! csys.hasDirectionCoordinate(), "Coordinate system has no direction coordinate" ); const uInt *oname; Int nall, nex; const auto *tname = MDirection::allMyTypes(nall, nex, oname); // Because MDirection::getType() only does minimal match, bogus strings // can tacitly be let through, so we do a more rigorous check here. ThrowIf( find( tname, tname+nall, dirRefFrameString) == tname+nall, "Unknown direction reference frame '" + dirRefFrameString + "'" ); ThrowIf ( ! MDirection::getType(_directionRefFrame, dirRefFrameString), "Unknown direction reference frame " + dirRefFrameString ); setFrequencyLimits( beginFreq, endFreq, freqRefFrame, dopplerString, restfreq ); _init(); } AnnotationBase::AnnotationBase( const Type type, const CoordinateSystem& csys, const Vector<Stokes::StokesTypes>& stokes ) : _type(type), _csys(csys), _label(DEFAULT_LABEL), _font(DEFAULT_FONT), _labelPos(DEFAULT_LABELPOS), _color(DEFAULT_COLOR), _labelColor(DEFAULT_LABELCOLOR), _fontstyle(DEFAULT_FONTSTYLE), _linestyle(DEFAULT_LINESTYLE), _fontsize(DEFAULT_FONTSIZE), _linewidth(DEFAULT_LINEWIDTH), _symbolsize(DEFAULT_SYMBOLSIZE), _symbolthickness(DEFAULT_SYMBOLTHICKNESS), _usetex(DEFAULT_USETEX), _convertedFreqLimits(0), _beginFreq(Quantity(0, "Hz")), _endFreq(Quantity(0, "Hz")), _restFreq(Quantity(0, "Hz")), _stokes(stokes), _globals(map<Keyword, Bool>()), _params(map<Keyword, String>()), _printGlobals(false), _labelOff(DEFAULT_LABELOFF) { String preamble = String(__FUNCTION__) + ": "; if (!csys.hasDirectionCoordinate()) { throw AipsError( preamble + "Coordinate system has no direction coordinate" ); } _directionRefFrame = _csys.directionCoordinate().directionType(); _init(); } AnnotationBase::~AnnotationBase() {} AnnotationBase& AnnotationBase::operator= ( const AnnotationBase& other ) { if (this == &other) { return *this; } _type = other._type; _directionRefFrame = other._directionRefFrame; _csys = other._csys; _directionAxes.resize(other._directionAxes.nelements()); _directionAxes = other._directionAxes; _convertedFreqLimits.assign(other._convertedFreqLimits); _beginFreq = other._beginFreq; _endFreq = other._endFreq; _restFreq = other._restFreq; _stokes.assign(other._stokes); _freqRefFrame = other._freqRefFrame; _dopplerType = other._dopplerType; _label = other._label; _color = other._color; _font = other._font; _fontsize = other._fontsize; _fontstyle = other._fontstyle; _linestyle = other._linestyle; _linewidth = other._linewidth; _symbolsize = other._symbolsize; _symbolthickness = other._symbolthickness; _usetex = other._usetex; _convertedDirections.assign(other._convertedDirections); _globals = other._globals; _params = other._params; _printGlobals = other._printGlobals; return *this; } void AnnotationBase::_init() { String preamble = _class + ": " + String(__FUNCTION__) + ": "; _initColors(); if ( _directionRefFrame != _csys.directionCoordinate().directionType(false) && _directionRefFrame != MDirection::B1950 && _directionRefFrame != MDirection::B1950_VLA && _directionRefFrame != MDirection::BMEAN && _directionRefFrame != MDirection::DEFAULT && _directionRefFrame != MDirection::ECLIPTIC && _directionRefFrame != MDirection::GALACTIC && _directionRefFrame != MDirection::J2000 && _directionRefFrame != MDirection::SUPERGAL && _directionRefFrame != MDirection::ICRS ) { throw AipsError(preamble + "Unsupported coordinate frame for regions " + MDirection::showType(_directionRefFrame) ); } _params[COORD] = MDirection::showType(_directionRefFrame); _directionAxes = IPosition(_csys.directionAxesNumbers()); uInt nStokes = _stokes.size(); if (nStokes > 0) { ostringstream os; os << "["; for (uInt i=0; i< nStokes; i++) { os << Stokes::name(_stokes[i]); if (i != _stokes.size() - 1) { os << ", "; } } os << "]"; _params[CORR] = os.str(); } for(uInt i=0; i<N_KEYS; i++) { _globals[(Keyword)i] = false; } _initParams(); } void AnnotationBase::_initParams() { _params[LINEWIDTH] = String::toString(_linewidth); _params[LINESTYLE] = lineStyleToString(_linestyle); _params[SYMSIZE] = String::toString(_symbolsize); _params[SYMTHICK] = String::toString(_symbolthickness); _params[COLOR] = getColorString(); _params[FONT] = _font; _params[FONTSIZE] = String::toString(_fontsize); _params[FONTSTYLE] = fontStyleToString(_fontstyle); _params[USETEX] = _usetex ? "true" : "false"; if (! _label.empty()) { _params[LABEL] = _label; } } void AnnotationBase::unitInit() { if (! _doneUnitInit) { UnitMap::putUser("pix",UnitVal(1.0), "pixel units"); UnitMap::putUser("channel",UnitVal(1.0), "channel number"); UnitMap::putUser("chan",UnitVal(1.0), "channel number"); _doneUnitInit = true; } } Bool AnnotationBase::setFrequencyLimits( const Quantity& beginFreq, const Quantity& endFreq, const String& freqRefFrame, const String& dopplerString, const Quantity& restfreq ) { String preamble(_class + ": " + String(__FUNCTION__) + ": "); if (beginFreq.getValue() == 0 && endFreq.getValue() == 0) { return false; } if (! getCsys().hasSpectralAxis()) { return false; } if ( beginFreq.getUnit().empty() && endFreq.getUnit().empty()) { throw AipsError( preamble + "Neither frequency specified has units. Both must" ); } if (! beginFreq.getUnit().empty() && endFreq.getUnit().empty()) { throw AipsError( preamble + "beginning frequency specified but ending frequency not. " + "Both must specified or both must be unspecified." ); } if (beginFreq.getUnit().empty() && ! endFreq.getUnit().empty()) { throw AipsError( preamble + "ending frequency specified but beginning frequency not. " + "Both must specified or both must be unspecified." ); } if (! beginFreq.getUnit().empty()) { if (! beginFreq.isConform(endFreq)) { throw AipsError( preamble + "Beginning freq units (" + beginFreq.getUnit() + ") do not conform to ending freq units (" + endFreq.getUnit() + ") but they must." ); } if ( ! beginFreq.isConform("Hz") && ! beginFreq.isConform("m/s") && ! beginFreq.isConform("pix") ) { throw AipsError( preamble + "Invalid frequency unit " + beginFreq.getUnit() ); } if (beginFreq.isConform("m/s") && restfreq.getValue() <= 0) { throw AipsError( preamble + "Beginning and ending velocities supplied but no restfreq specified" ); } if (freqRefFrame.empty()) { _freqRefFrame = getCsys().spectralCoordinate().frequencySystem(); } else if (! MFrequency::getType(_freqRefFrame, freqRefFrame)) { throw AipsError( preamble + "Unknown frequency frame code " + freqRefFrame ); } else { _setParam(AnnotationBase::FRAME, freqRefFrame); } if (dopplerString.empty()) { _dopplerType = getCsys().spectralCoordinate().velocityDoppler(); } else if (! MDoppler::getType(_dopplerType, dopplerString)) { throw AipsError( preamble + "Unknown doppler code " + dopplerString ); } else { _setParam(AnnotationBase::VELTYPE, dopplerString); } _beginFreq = beginFreq; _endFreq = endFreq; _restFreq = restfreq; _setParam(AnnotationBase::RANGE, _printFreqRange()); _setParam(AnnotationBase::RESTFREQ, _printFreq(_restFreq)); _checkAndConvertFrequencies(); return true; } return false; } Vector<MFrequency> AnnotationBase::getFrequencyLimits() const { return _convertedFreqLimits; } Vector<Stokes::StokesTypes> AnnotationBase::getStokes() const { return _stokes; } void AnnotationBase::_checkAndConvertFrequencies() { const CoordinateSystem& csys = getCsys(); const SpectralCoordinate spcoord = csys.spectralCoordinate(); MFrequency::Types cFrameType = spcoord.frequencySystem(false); MDoppler::Types cDopplerType = spcoord.velocityDoppler(); _convertedFreqLimits.resize(2); for (Int i=0; i<2; i++) { Quantity qFreq = i == 0 ? _beginFreq : _endFreq; String unit = qFreq.getUnit(); if (qFreq.isConform("pix")) { Int spectralAxisNumber = csys.spectralAxisNumber(true); String unit = csys.worldAxisUnits()[spectralAxisNumber]; Double world; if (! spcoord.toWorld(world, qFreq.getValue())) { ostringstream os; os << String(__FUNCTION__) << ": Unable to convert pixel to world value " << "for spectral coordinate"; throw AipsError(os.str()); } if (_freqRefFrame != cFrameType) { LogIO log; log << LogOrigin(String(__FUNCTION__)) << LogIO::WARN << ": Frequency range given in pixels but supplied frequency ref frame (" << MFrequency::showType(_freqRefFrame) << ") differs from that of " << "the provided coordinate system (" << MFrequency::showType(cFrameType) << "). The provided frequency range will therefore be assumed to already " << "be in the coordinate system frequency reference frame and no conversion " << "will be done" << LogIO::POST; } if (_dopplerType != cDopplerType) { LogIO log; log << LogOrigin(String(__FUNCTION__)) << LogIO::WARN << ": Frequency range given in pixels but supplied doppler type (" << MDoppler::showType(_dopplerType) << ") differs from that of " << "the provided coordinate system (" << MDoppler::showType(cDopplerType) << "). The provided frequency range will therefore be assumed to already " << "be in the coordinate system doppler and no conversion " << "will be done" << LogIO::POST; } _freqRefFrame = cFrameType; _dopplerType = cDopplerType; _convertedFreqLimits[i] = MFrequency( Quantity(world, unit), _freqRefFrame ); } else if (qFreq.isConform("m/s")) { MFrequency::Ref freqRef(_freqRefFrame); MDoppler::Ref velRef(_dopplerType); VelocityMachine vm(freqRef, Unit("GHz"), MVFrequency(_restFreq), velRef, unit ); qFreq = vm(qFreq); _convertedFreqLimits[i] = MFrequency(qFreq, _freqRefFrame); if (_dopplerType != cDopplerType) { MDoppler dopplerConversion = MDoppler::Convert(_dopplerType, cDopplerType)(); _convertedFreqLimits[i] = MFrequency::fromDoppler( dopplerConversion, _convertedFreqLimits[i].get("Hz"), cFrameType ); } } else if ( qFreq.isConform("Hz")) { _convertedFreqLimits[i] = MFrequency(qFreq, _freqRefFrame); } else { throw AipsError("Logic error. Bad spectral unit " + unit + " somehow made it to a place where it shouldn't have" ); } if (_freqRefFrame != cFrameType) { Vector<Double> refDirection = csys.directionCoordinate().referenceValue(); Vector<String> directionUnits = csys.directionCoordinate().worldAxisUnits(); MDirection refDir( Quantity(refDirection[0], directionUnits[0]), Quantity(refDirection[1], directionUnits[1]), getCsys().directionCoordinate().directionType() ); MFrequency::Ref inFrame(_freqRefFrame, MeasFrame(refDir)); MFrequency::Ref outFrame(cFrameType, MeasFrame(refDir)); MFrequency::Convert converter(inFrame, outFrame); _convertedFreqLimits[i] = converter(_convertedFreqLimits[i]); } } } String AnnotationBase::_printFreqRange() const { ostringstream os; os << "[" << _printFreq(_beginFreq) << ", " << _printFreq(_endFreq) << "]"; return os.str(); } String AnnotationBase::_printFreq(const Quantity& freq) { if (freq.isConform("pix")) { return _printPixel(freq.getValue()); } ostringstream os; os << std::fixed; if (freq.isConform("km/s")) { os << std::setprecision(4) << freq.getValue("km/s") << "km/s"; } else { os << std::setprecision(3) << freq.getValue("MHz") << "MHz"; } return os.str(); } AnnotationBase::Type AnnotationBase::getType() const { return _type; } void AnnotationBase::_initTypeMap() { _typeMap["line"] = LINE; _typeMap["vector"] = VECTOR; _typeMap["text"] = TEXT; _typeMap["symbol"] = SYMBOL; _typeMap["box"] = RECT_BOX; _typeMap["rectangularbox"] = RECT_BOX; _typeMap["centerbox"] = CENTER_BOX; _typeMap["rotatedbox"] = ROTATED_BOX; _typeMap["rotbox"] = ROTATED_BOX; _typeMap["poly"] = POLYGON; _typeMap["polygon"] = POLYGON; _typeMap["circle"] = CIRCLE; _typeMap["annulus"] = ANNULUS; _typeMap["ellipse"] = ELLIPSE; } AnnotationBase::Type AnnotationBase::typeFromString( const String& type ) { if (_typeMap.size() == 0) { _initTypeMap(); } String cType = type; cType.downcase(); cType.trim(); if (_typeMap.find(cType) == _typeMap.end()) { throw AipsError(type + " is not a supported annotation type"); } return _typeMap.at(cType); } String AnnotationBase::typeToString(const AnnotationBase::Type type) { if (_typeMap.size() == 0) { _initTypeMap(); } for ( map<String, Type>::const_iterator iter = _typeMap.begin(); iter != _typeMap.end(); iter++ ) { if (iter->second == type) { return iter->first; } } throw AipsError( _class + "::" + __FUNCTION__ + ": Logic error. Type " + String::toString(type) + " not handled" ); } AnnotationBase::LineStyle AnnotationBase::lineStyleFromString(const String& ls) { if (_lineStyleMap.size() == 0) { _lineStyleMap["-"] = SOLID; _lineStyleMap["--"] = DASHED; _lineStyleMap["-."] = DOT_DASHED; _lineStyleMap[":"] = DOTTED; } String cls = ls; cls.trim(); if (cls.empty()) { return DEFAULT_LINESTYLE; } if (_lineStyleMap.find(cls) == _lineStyleMap.end()) { throw AipsError( ls + " is not a supported line style" ); } return _lineStyleMap.at(cls); } AnnotationBase::FontStyle AnnotationBase::fontStyleFromString(const String& fs) { String cfs = fs; cfs.downcase(); cfs.trim(); // FIXME when nothing to do and feeling anal, turn this into // a static map if (cfs.empty()) { return DEFAULT_FONTSTYLE; } else if (cfs == "normal") { return NORMAL; } else if (cfs == "bold") { return BOLD; } else if (cfs == "italic") { return ITALIC; } else if (cfs == "bold-italic") { return ITALIC_BOLD; } else { throw AipsError( fs + " is not a supported font style" ); } } String AnnotationBase::fontStyleToString( const AnnotationBase::FontStyle fs ) { switch (fs) { case NORMAL: return "normal"; case BOLD: return "bold"; case ITALIC: return "italic"; case ITALIC_BOLD: return "itatlic_bold"; default: throw AipsError( _class + ": " + String(__FUNCTION__) + ": " + ": Logic error, should never have gotten here" ); } } void AnnotationBase::setLabel(const String& s) { _label = s; if (_label.empty()) { if (_params.find(LABEL) != _params.end()) { _params.erase(LABEL); } } else { _params[LABEL] = _label; } } String AnnotationBase::getLabel() const { return _label; } Bool AnnotationBase::_isRGB(const AnnotationBase::RGB& rgb) { if (rgb.size() != 3) { return false; } for (RGB::const_iterator iter=rgb.begin(); iter!=rgb.end(); iter++) { if (*iter < 0 || *iter > 255) { return false; } } return true; } AnnotationBase::RGB AnnotationBase::_colorStringToRGB(const String& s) { String c = s; c.trim(); c.downcase(); if (_colors.find(c) != _colors.end()) { return _colors.find(c)->second; } else if (c.find(rgbHexRegex) != String::npos) { RGB rgb(3); for (uInt i=0; i<3; i++) { String comp = s.substr(2*i, 2); int hexInt; sscanf(comp.c_str(), "%x", &hexInt ); rgb[i] = hexInt; } return rgb; } else { throw AipsError("Unrecognized color specification " + s); } } void AnnotationBase::setColor(const String& s) { _color = _colorStringToRGB(s); _params[COLOR] = colorToString(_color); } void AnnotationBase::setColor(const RGB& rgb) { if (! _isRGB(rgb)) { throw AipsError( _class + "::" + __FUNCTION__ + ": input vector is not a valid RGB representation" ); } _color = rgb; _params[COLOR] = colorToString(_color); } AnnotationBase::RGB AnnotationBase::getColor() const { return _color; } String AnnotationBase::getColorString() const { return colorToString(_color); } String AnnotationBase::colorToString(const AnnotationBase::RGB& color) { if (! _isRGB(color)) { throw AipsError( _class + "::" + __FUNCTION__ + ": input vector is not a valid RGB representation" ); } if (_rgbNameMap.find(color) != _rgbNameMap.end()) { return _rgbNameMap.find(color)->second; } else { ostringstream oss; oss << hex << std::setw(2) << std::setfill('0') << (Int)floor(color[0] + 0.5) << hex << std::setw(2) << std::setfill('0') << (Int)floor(color[1] + 0.5) << hex << std::setw(2) << std::setfill('0') << (Int)floor(color[2] + 0.5); String rgbString = oss.str(); rgbString.downcase(); return rgbString; } } void AnnotationBase::setLabelColor(const String& color) { _labelColor = _colorStringToRGB(color); _params[LABELCOLOR] = color; } void AnnotationBase::setLabelColor(const RGB& color) { if (! _isRGB(color)) { throw AipsError( _class + "::" + __FUNCTION__ + ": input vector is not a valid RGB representation" ); } _labelColor = color; _params[LABELCOLOR] = colorToString(_labelColor); } String AnnotationBase::getLabelColorString() const { return colorToString(_labelColor); } AnnotationBase::RGB AnnotationBase::getLabelColor() const { return _labelColor; } void AnnotationBase::setLineStyle(const LineStyle s) { _linestyle = s; _params[LINESTYLE] = lineStyleToString(_linestyle); } AnnotationBase::LineStyle AnnotationBase::getLineStyle() const { return _linestyle; } void AnnotationBase::setLineWidth(const uInt s) { _linewidth = s; _params[LINEWIDTH] = String::toString(_linewidth); } uInt AnnotationBase::getLineWidth() const { return _linewidth; } void AnnotationBase::setSymbolSize(const uInt s) { _symbolsize = s; _params[SYMSIZE] = String::toString(_symbolsize); } uInt AnnotationBase::getSymbolSize() const { return _symbolsize; } void AnnotationBase::setSymbolThickness(const uInt s) { _symbolthickness = s; _params[SYMTHICK] = String::toString(_symbolthickness); } uInt AnnotationBase::getSymbolThickness() const { return _symbolthickness; } void AnnotationBase::setFont(const String& s) { _font = s; _params[FONT] = _font; } String AnnotationBase::getFont() const { return _font; } void AnnotationBase::setFontSize(const uInt s) { _fontsize = s; _params[FONTSIZE] = String::toString(_fontsize); } uInt AnnotationBase::getFontSize() const { return _fontsize; } void AnnotationBase::setFontStyle(const AnnotationBase::FontStyle& fs) { _fontstyle = fs; _params[FONTSTYLE] = fontStyleToString(_fontstyle); } AnnotationBase::FontStyle AnnotationBase::getFontStyle() const { return _fontstyle; } void AnnotationBase::setUseTex(const Bool s) { _usetex = s; _params[USETEX] = _usetex ? "true" : "false"; } Bool AnnotationBase::isUseTex() const { return _usetex; } String AnnotationBase::getLabelPosition() const { return _labelPos; } void AnnotationBase::setLabelPosition(const String& position) { String c = position; c.trim(); c.downcase(); if ( c != "top" && c != "bottom" && c != "left" && c != "right" ) { throw AipsError( _class + "::" + __FUNCTION__ + ": Unknown label position " + position ); } _labelPos = c; _params[LABELPOS] = _labelPos; } void AnnotationBase::setLabelOffset(const vector<Int>& offset) { if (offset.size() != 2) { throw AipsError( _class + "::" + __FUNCTION__ + ": Number of elements in label offset must be exactly 2, not " + String(offset.size()) ); } _labelOff = offset; _params[LABELOFF] = "[" + String::toString(offset[0]) + ", " + String::toString(offset[1]) + "]"; } vector<Int> AnnotationBase::getLabelOffset() const { return _labelOff; } Bool AnnotationBase::isRegion() const { return false; } void AnnotationBase::setGlobals( const Vector<Keyword>& globalKeys ) { for ( Vector<Keyword>::const_iterator iter=globalKeys.begin(); iter != globalKeys.end(); iter++) { _globals[*iter] = true; } } String AnnotationBase::keywordToString( const Keyword key ) { switch(key) { case COORD: return "coord"; case RANGE: return "range"; case FRAME: return "frame"; case CORR: return "corr"; case VELTYPE: return "veltype"; case RESTFREQ: return "restfreq"; case LINEWIDTH: return "linewidth"; case LINESTYLE: return "linestyle"; case SYMSIZE: return "symsize"; case SYMTHICK: return "symthick"; case COLOR: return "color"; case FONT: return "font"; case FONTSIZE: return "fontsize"; case FONTSTYLE: return "fontstyle"; case USETEX: return "usetex"; case LABEL: return "label"; case LABELCOLOR: return "labelcolor"; case LABELPOS: return "labelpos"; case LABELOFF: return "labeloff"; case UNKNOWN_KEYWORD: case N_KEYS: default: throw AipsError( _class + "::" + __FUNCTION__ + ": Logic error: No string representation for Keyword " + String(key) ); } } String AnnotationBase::lineStyleToString( const LineStyle style ) { switch(style) { case SOLID: return "-"; case DASHED: return "--"; case DOT_DASHED: return "-."; case DOTTED: return ":"; default: ThrowCc( "Logic error: No string representation for LineStyle " + String::toString(style) ); } } ostream& AnnotationBase::print( ostream& os, const LineStyle ls ) { os << lineStyleToString(ls); return os; } ostream& AnnotationBase::print( ostream& os, const FontStyle fs ) { os << fontStyleToString(fs); return os; } ostream& AnnotationBase::print( ostream& os, const map<Keyword, String>& params ) { if (params.size() == 0) { return os; } Bool hasLabel = params.find(LABEL) != params.end(); for ( map<Keyword, String>::const_iterator iter=params.begin(); iter!=params.end(); iter++ ) { Keyword key = iter->first; if (! iter->second.empty()) { if ( ! hasLabel && ( key == LABELCOLOR || key == LABELPOS || key == LABELOFF ) ) { continue; } if (iter != params.begin()) { os << ", "; } String quote = key == LABEL || ( iter->second.contains(' ') && (key != RANGE && key != CORR && key != LABELOFF) ) ? "\"" : ""; os << keywordToString((Keyword)iter->first) << "=" << quote << iter->second << quote; } } return os; } ostream& AnnotationBase::print( ostream& os, const Direction d ) { for (uInt i=0; i<d.size(); i++) { os << i << ": " << d[i].first << ", " << d[i].second << endl; } return os; } void AnnotationBase::_printPairs(ostream &os) const { map<Keyword, String> x = _params; if (! _printGlobals) { for ( map<Keyword, String>::const_iterator iter = _params.begin(); iter != _params.end(); iter++ ) { Keyword k = iter->first; if (_globals.find(k) != _globals.end() && _globals.at(k)) { x.erase(k); } } } if (x.size() > 0) { os << " " << x; } } void AnnotationBase::_checkMixed( const String& origin, const AnnotationBase::Direction& quantities ) { Bool isWorld = false; Bool isPixel = false; Quantity qArg; for ( Direction::const_iterator iter = quantities.begin(); iter != quantities.end(); iter++ ) { for (uInt i=0; i<2; i++) { Quantity tQ = i == 0 ? iter->first : iter->second; Bool pix = tQ.getUnit() == "pix"; Bool world = ! pix; isWorld = isWorld || world; isPixel = isPixel || pix; if (isPixel && isWorld) { throw AipsError( origin + ": Mixed world and pixel coordinates not supported" ); } } } } MDirection AnnotationBase::_directionFromQuantities( const Quantity& q0, const Quantity& q1 ) { ostringstream oss; oss << q0 << ", " << q1; Quantity d0 = q0; Quantity d1 = q1; String value = oss.str(); if (q0.getUnit() == "pix") { // both quantities are in pix, this check should // have been done prior to calling this method Vector<Double> pixel(_csys.nPixelAxes(), 0); pixel[_directionAxes[0]] = q0.getValue(); pixel[_directionAxes[1]] = q1.getValue(); Vector<Double> world; _csys.toWorld(world, pixel); Vector<String> axesUnits = _csys.worldAxisUnits(); d0 = Quantity(world[_directionAxes[0]], axesUnits[_directionAxes[0]]); d1 = Quantity(world[_directionAxes[1]], axesUnits[_directionAxes[1]]); MDirection::Types csysDirectionType = _csys.directionCoordinate().directionType(false); if (_directionRefFrame != csysDirectionType) { LogIO log; log << LogOrigin(String(__FUNCTION__)) << LogIO::WARN << ": Direction quantities specified in pixels but specified direction reference " << "frame (" << MDirection::showType(_directionRefFrame) << ") is different from " << "the reference frame (" << MDirection::showType(csysDirectionType) << ") of the coordinate system. The reference frame of the coordinate system " << "will be used and the direction coordinates will not be transformed" << LogIO::POST; } _directionRefFrame = csysDirectionType; } try { return MDirection(d0, d1, _directionRefFrame); } catch (const AipsError& x) { throw AipsError( _class + "::" + String(__FUNCTION__) + ": Error converting direction (" + value + ") to MDirection: " + x.getMesg() ); } } void AnnotationBase::_checkAndConvertDirections( const String& origin, const AnnotationBase::Direction& quantities ) { _checkMixed(origin, quantities); MDirection::Types csysDirectionRefFrame = _csys.directionCoordinate().directionType(false); Bool needsConverting = _directionRefFrame != csysDirectionRefFrame; _convertedDirections.resize(quantities.size()); for (uInt i=0; i<quantities.size(); i++) { _convertedDirections[i] = _directionFromQuantities(quantities(i).first, quantities(i).second); if (needsConverting) { _convertedDirections[i] = MDirection::Convert(_convertedDirections[i], csysDirectionRefFrame)(); } } // check this now because if converting from world to pixel fails when // regions are being formed, it will wreak havoc _testConvertToPixel(); } AnnotationBase::Direction AnnotationBase::getDirections() const { Direction res(_convertedDirections.size()); for (uInt i=0; i<res.size(); i++) { Quantum<Vector<Double> > angles = _convertedDirections[i].getAngle(); String unit = angles.getUnit(); Vector<Double> vals = angles.getValue(); res[i].first = Quantity(vals[0], unit); res[i].second = Quantity(vals[1], unit); } return res; } void AnnotationBase::_initColors() { if (_doneColorInit) { return; } _colors.insert(make_pair("black", BLACK)); _colors.insert(make_pair("blue", BLUE)); _colors.insert(make_pair("cyan", CYAN)); _colors.insert(make_pair("gray", GRAY)); _colors.insert(make_pair("green", GREEN)); _colors.insert(make_pair("magenta", MAGENTA)); _colors.insert(make_pair("orange", ORANGE)); _colors.insert(make_pair("red", RED)); _colors.insert(make_pair("white", WHITE)); _colors.insert(make_pair("yellow", YELLOW)); for ( map<string, RGB>::const_iterator iter=_colors.begin(); iter != _colors.end(); iter++ ) { _rgbNameMap[iter->second] = iter->first; _colorNames.push_back(iter->first); } _doneColorInit = true; } std::list<std::string> AnnotationBase::colorChoices() { _initColors(); return _colorNames; } void AnnotationBase::_testConvertToPixel() const { Vector<Double> pixel(2); Vector<Double> world(2); const auto units = _csys.worldAxisUnits(); const auto end = _convertedDirections.end(); for (auto iter = _convertedDirections.begin(); iter != end; ++iter) { world[0] = iter->getAngle().getValue(units[0])[0]; world[1] = iter->getAngle().getValue(units[1])[1]; if (! _csys.directionCoordinate().toPixel(pixel, world)) { ostringstream oss; oss << "Could not convert world coordinate " << world << "to pixel"; throw (WorldToPixelConversionError(oss.str())); } } } String AnnotationBase::_printDirection( const Quantity& longitude, const Quantity& latitude ) const { if (longitude.getUnit() == "pix") { ostringstream os; os << _printPixel(longitude.getValue()) << ", " << _printPixel(latitude.getValue()); return os.str(); } MDirection::Types frame; MDirection::getType(frame, _params.find(COORD)->second); if ( frame == MDirection::J2000 || frame == MDirection::B1950 || frame == MDirection::JMEAN || frame == MDirection::JTRUE || frame == MDirection::B1950_VLA || frame == MDirection::BMEAN || frame == MDirection::BTRUE ) { // equatorial coordinates in sexigesimal MVAngle x(longitude); MVAngle y(latitude); return x.string(MVAngle::TIME_CLEAN, 11) + ", " + y.string(MVAngle::ANGLE, 10); } else { // non-equatorial coordinates in degrees return _toDeg(longitude) + ", " + _toDeg(latitude); } } String AnnotationBase::_toArcsec(const Quantity& angle) { ostringstream os; if (angle.getUnit() == "pix") { os << _printPixel(angle.getValue()); } else { os << std::fixed << std::setprecision(4) << angle.getValue("arcsec") << "arcsec"; } return os.str(); } String AnnotationBase::_toDeg(const Quantity& angle) { ostringstream os; if (angle.getUnit() == "pix") { os << _printPixel(angle.getValue()); } else { os << std::fixed << std::setprecision(8) << angle.getValue("deg") << "deg"; } return os.str(); } String AnnotationBase::_printPixel(const Double& d) { ostringstream os; os << std::fixed << std::setprecision(1) << d << "pix"; return os.str(); } }