Line data Source code
1 : //# GaussianShape.cc:
2 : //# Copyright (C) 1998,1999,2000
3 : //# Associated Universities, Inc. Washington DC, USA.
4 : //#
5 : //# This library is free software; you can redistribute it and/or modify it
6 : //# under the terms of the GNU Library General Public License as published by
7 : //# the Free Software Foundation; either version 2 of the License, or (at your
8 : //# option) any later version.
9 : //#
10 : //# This library is distributed in the hope that it will be useful, but WITHOUT
11 : //# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
12 : //# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public
13 : //# License for more details.
14 : //#
15 : //# You should have received a copy of the GNU Library General Public License
16 : //# along with this library; if not, write to the Free Software Foundation,
17 : //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA.
18 : //#
19 : //# Correspondence concerning AIPS++ should be addressed as follows:
20 : //# Internet email: aips2-request@nrao.edu.
21 : //# Postal address: AIPS++ Project Office
22 : //# National Radio Astronomy Observatory
23 : //# 520 Edgemont Road
24 : //# Charlottesville, VA 22903-2475 USA
25 : //#
26 :
27 : #include <imageanalysis/Annotations/AnnotationBase.h>
28 :
29 : #include <casacore/casa/Exceptions/Error.h>
30 : #include <casacore/casa/Quanta/MVAngle.h>
31 : #include <casacore/coordinates/Coordinates/DirectionCoordinate.h>
32 : #include <casacore/coordinates/Coordinates/SpectralCoordinate.h>
33 : #include <casacore/measures/Measures/MCDirection.h>
34 : #include <casacore/measures/Measures/VelocityMachine.h>
35 : #include <casacore/casa/Quanta/UnitMap.h>
36 :
37 : #include <iomanip>
38 :
39 : using namespace std;
40 :
41 : using namespace casacore;
42 : namespace casa {
43 :
44 : const AnnotationBase::RGB AnnotationBase::BLACK(3, 0.0);
45 : const AnnotationBase::RGB AnnotationBase::BLUE{0.0,0.0,255.0};
46 : const AnnotationBase::RGB AnnotationBase::CYAN{255.0,255.0,0.0};
47 : const AnnotationBase::RGB AnnotationBase::GRAY{190.0,190.0,190.0};
48 : const AnnotationBase::RGB AnnotationBase::GREEN{0.0,255.0,0.0};
49 : const AnnotationBase::RGB AnnotationBase::MAGENTA{255.0,0.0,255.0};
50 : const AnnotationBase::RGB AnnotationBase::ORANGE{255.0,165.0,0.0};
51 : const AnnotationBase::RGB AnnotationBase::RED{255.0,0.0,0.0};
52 : const AnnotationBase::RGB AnnotationBase::WHITE(3, 255.0);
53 : const AnnotationBase::RGB AnnotationBase::YELLOW{255.0,255.0,0.0};
54 :
55 : const String AnnotationBase::DEFAULT_LABEL = "";
56 : const AnnotationBase::RGB AnnotationBase::DEFAULT_COLOR = AnnotationBase::GREEN;
57 : const AnnotationBase::LineStyle AnnotationBase::DEFAULT_LINESTYLE = AnnotationBase::SOLID;
58 : const uInt AnnotationBase::DEFAULT_LINEWIDTH = 1;
59 : const uInt AnnotationBase::DEFAULT_SYMBOLSIZE = 1;
60 : const uInt AnnotationBase::DEFAULT_SYMBOLTHICKNESS = 1;
61 : const String AnnotationBase::DEFAULT_FONT = "Helvetica";
62 : const uInt AnnotationBase::DEFAULT_FONTSIZE = 10;
63 : const AnnotationBase::FontStyle AnnotationBase::DEFAULT_FONTSTYLE = AnnotationBase::BOLD;
64 : const Bool AnnotationBase::DEFAULT_USETEX = false;
65 : const AnnotationBase::RGB AnnotationBase::DEFAULT_LABELCOLOR = AnnotationBase::GREEN;
66 : const String AnnotationBase::DEFAULT_LABELPOS = "top";
67 : const vector<Int> AnnotationBase::DEFAULT_LABELOFF = vector<Int>(2, 0);
68 :
69 : const String AnnotationBase::_class = "AnnotationBase";
70 :
71 : std::list<string> AnnotationBase::_colorNames;
72 :
73 : Bool AnnotationBase::_doneUnitInit = false;
74 : Bool AnnotationBase::_doneColorInit = false;
75 :
76 : map<String, AnnotationBase::Type> AnnotationBase::_typeMap;
77 : map<String, AnnotationBase::LineStyle> AnnotationBase::_lineStyleMap;
78 :
79 : map<string, AnnotationBase::RGB> AnnotationBase::_colors;
80 : map<AnnotationBase::RGB, string> AnnotationBase::_rgbNameMap;
81 :
82 : const Regex AnnotationBase::rgbHexRegex("([0-9]|[a-f]){6}");
83 :
84 :
85 0 : AnnotationBase::AnnotationBase(
86 : const Type type, const String& dirRefFrameString,
87 : const CoordinateSystem& csys, const Quantity& beginFreq,
88 : const Quantity& endFreq,
89 : const String& freqRefFrame,
90 : const String& dopplerString,
91 : const Quantity& restfreq,
92 : const Vector<Stokes::StokesTypes>& stokes
93 0 : )
94 : : _type(type), _csys(csys), _label(DEFAULT_LABEL),_font(DEFAULT_FONT),
95 : _labelPos(DEFAULT_LABELPOS), _color(DEFAULT_COLOR), _labelColor(DEFAULT_LABELCOLOR),
96 : _fontstyle(DEFAULT_FONTSTYLE), _linestyle(DEFAULT_LINESTYLE), _fontsize(DEFAULT_FONTSIZE),
97 : _linewidth(DEFAULT_LINEWIDTH), _symbolsize(DEFAULT_SYMBOLSIZE),
98 : _symbolthickness(DEFAULT_SYMBOLTHICKNESS), _usetex(DEFAULT_USETEX),
99 :
100 : _convertedFreqLimits(0), _stokes(stokes),
101 : _globals(map<Keyword, Bool>()), _params(map<Keyword, String>()),
102 0 : _printGlobals(false), _labelOff(DEFAULT_LABELOFF) {
103 0 : ThrowIf(
104 : ! csys.hasDirectionCoordinate(),
105 : "Coordinate system has no direction coordinate"
106 : );
107 : const uInt *oname;
108 : Int nall, nex;
109 0 : const auto *tname = MDirection::allMyTypes(nall, nex, oname);
110 : // Because MDirection::getType() only does minimal match, bogus strings
111 : // can tacitly be let through, so we do a more rigorous check here.
112 0 : ThrowIf(
113 : find( tname, tname+nall, dirRefFrameString) == tname+nall,
114 : "Unknown direction reference frame '" + dirRefFrameString + "'"
115 : );
116 0 : ThrowIf (
117 : ! MDirection::getType(_directionRefFrame, dirRefFrameString),
118 : "Unknown direction reference frame " + dirRefFrameString
119 : );
120 0 : setFrequencyLimits(
121 : beginFreq, endFreq, freqRefFrame,
122 : dopplerString, restfreq
123 : );
124 0 : _init();
125 0 : }
126 :
127 0 : AnnotationBase::AnnotationBase(
128 : const Type type, const CoordinateSystem& csys,
129 : const Vector<Stokes::StokesTypes>& stokes
130 0 : )
131 : : _type(type), _csys(csys), _label(DEFAULT_LABEL),
132 : _font(DEFAULT_FONT), _labelPos(DEFAULT_LABELPOS),
133 : _color(DEFAULT_COLOR), _labelColor(DEFAULT_LABELCOLOR),
134 : _fontstyle(DEFAULT_FONTSTYLE), _linestyle(DEFAULT_LINESTYLE),
135 : _fontsize(DEFAULT_FONTSIZE),
136 : _linewidth(DEFAULT_LINEWIDTH), _symbolsize(DEFAULT_SYMBOLSIZE),
137 : _symbolthickness(DEFAULT_SYMBOLTHICKNESS), _usetex(DEFAULT_USETEX),
138 : _convertedFreqLimits(0), _beginFreq(Quantity(0, "Hz")), _endFreq(Quantity(0, "Hz")),
139 : _restFreq(Quantity(0, "Hz")), _stokes(stokes),
140 : _globals(map<Keyword, Bool>()), _params(map<Keyword, String>()),
141 0 : _printGlobals(false), _labelOff(DEFAULT_LABELOFF)
142 : {
143 0 : String preamble = String(__FUNCTION__) + ": ";
144 0 : if (!csys.hasDirectionCoordinate()) {
145 : throw AipsError(
146 0 : preamble + "Coordinate system has no direction coordinate"
147 0 : );
148 : }
149 0 : _directionRefFrame = _csys.directionCoordinate().directionType();
150 0 : _init();
151 0 : }
152 :
153 0 : AnnotationBase::~AnnotationBase() {}
154 :
155 0 : AnnotationBase& AnnotationBase::operator= (
156 : const AnnotationBase& other
157 : ) {
158 0 : if (this == &other) {
159 0 : return *this;
160 : }
161 0 : _type = other._type;
162 0 : _directionRefFrame = other._directionRefFrame;
163 0 : _csys = other._csys;
164 0 : _directionAxes.resize(other._directionAxes.nelements());
165 0 : _directionAxes = other._directionAxes;
166 0 : _convertedFreqLimits.assign(other._convertedFreqLimits);
167 0 : _beginFreq = other._beginFreq;
168 0 : _endFreq = other._endFreq;
169 0 : _restFreq = other._restFreq;
170 0 : _stokes.assign(other._stokes);
171 0 : _freqRefFrame = other._freqRefFrame;
172 0 : _dopplerType = other._dopplerType;
173 0 : _label = other._label;
174 0 : _color = other._color;
175 0 : _font = other._font;
176 0 : _fontsize = other._fontsize;
177 0 : _fontstyle = other._fontstyle;
178 0 : _linestyle = other._linestyle;
179 0 : _linewidth = other._linewidth;
180 0 : _symbolsize = other._symbolsize;
181 0 : _symbolthickness = other._symbolthickness;
182 0 : _usetex = other._usetex;
183 0 : _convertedDirections.assign(other._convertedDirections);
184 0 : _globals = other._globals;
185 0 : _params = other._params;
186 0 : _printGlobals = other._printGlobals;
187 0 : return *this;
188 : }
189 :
190 0 : void AnnotationBase::_init() {
191 0 : String preamble = _class + ": " + String(__FUNCTION__) + ": ";
192 0 : _initColors();
193 0 : if (
194 0 : _directionRefFrame != _csys.directionCoordinate().directionType(false)
195 0 : && _directionRefFrame != MDirection::B1950
196 0 : && _directionRefFrame != MDirection::B1950_VLA
197 0 : && _directionRefFrame != MDirection::BMEAN
198 0 : && _directionRefFrame != MDirection::DEFAULT
199 0 : && _directionRefFrame != MDirection::ECLIPTIC
200 0 : && _directionRefFrame != MDirection::GALACTIC
201 0 : && _directionRefFrame != MDirection::J2000
202 0 : && _directionRefFrame != MDirection::SUPERGAL
203 0 : && _directionRefFrame != MDirection::ICRS
204 : ) {
205 : throw AipsError(preamble
206 0 : + "Unsupported coordinate frame for regions "
207 0 : + MDirection::showType(_directionRefFrame)
208 0 : );
209 : }
210 0 : _params[COORD] = MDirection::showType(_directionRefFrame);
211 0 : _directionAxes = IPosition(_csys.directionAxesNumbers());
212 :
213 0 : uInt nStokes = _stokes.size();
214 0 : if (nStokes > 0) {
215 0 : ostringstream os;
216 0 : os << "[";
217 0 : for (uInt i=0; i< nStokes; i++) {
218 0 : os << Stokes::name(_stokes[i]);
219 0 : if (i != _stokes.size() - 1) {
220 0 : os << ", ";
221 : }
222 : }
223 0 : os << "]";
224 0 : _params[CORR] = os.str();
225 : }
226 0 : for(uInt i=0; i<N_KEYS; i++) {
227 0 : _globals[(Keyword)i] = false;
228 : }
229 0 : _initParams();
230 0 : }
231 :
232 0 : void AnnotationBase::_initParams() {
233 0 : _params[LINEWIDTH] = String::toString(_linewidth);
234 0 : _params[LINESTYLE] = lineStyleToString(_linestyle);
235 0 : _params[SYMSIZE] = String::toString(_symbolsize);
236 0 : _params[SYMTHICK] = String::toString(_symbolthickness);
237 0 : _params[COLOR] = getColorString();
238 0 : _params[FONT] = _font;
239 0 : _params[FONTSIZE] = String::toString(_fontsize);
240 0 : _params[FONTSTYLE] = fontStyleToString(_fontstyle);
241 0 : _params[USETEX] = _usetex ? "true" : "false";
242 0 : if (! _label.empty()) {
243 0 : _params[LABEL] = _label;
244 : }
245 0 : }
246 :
247 0 : void AnnotationBase::unitInit() {
248 0 : if (! _doneUnitInit) {
249 0 : UnitMap::putUser("pix",UnitVal(1.0), "pixel units");
250 0 : UnitMap::putUser("channel",UnitVal(1.0), "channel number");
251 0 : UnitMap::putUser("chan",UnitVal(1.0), "channel number");
252 0 : _doneUnitInit = true;
253 : }
254 0 : }
255 :
256 :
257 0 : Bool AnnotationBase::setFrequencyLimits(
258 : const Quantity& beginFreq,
259 : const Quantity& endFreq,
260 : const String& freqRefFrame,
261 : const String& dopplerString,
262 : const Quantity& restfreq
263 : ) {
264 0 : String preamble(_class + ": " + String(__FUNCTION__) + ": ");
265 0 : if (beginFreq.getValue() == 0 && endFreq.getValue() == 0) {
266 0 : return false;
267 : }
268 0 : if (! getCsys().hasSpectralAxis()) {
269 0 : return false;
270 : }
271 0 : if ( beginFreq.getUnit().empty() && endFreq.getUnit().empty()) {
272 : throw AipsError(
273 0 : preamble + "Neither frequency specified has units. Both must"
274 0 : );
275 : }
276 0 : if (! beginFreq.getUnit().empty() && endFreq.getUnit().empty()) {
277 : throw AipsError(
278 0 : preamble + "beginning frequency specified but ending frequency not. "
279 0 : + "Both must specified or both must be unspecified."
280 0 : );
281 : }
282 0 : if (beginFreq.getUnit().empty() && ! endFreq.getUnit().empty()) {
283 : throw AipsError(
284 0 : preamble + "ending frequency specified but beginning frequency not. "
285 0 : + "Both must specified or both must be unspecified."
286 0 : );
287 : }
288 0 : if (! beginFreq.getUnit().empty()) {
289 0 : if (! beginFreq.isConform(endFreq)) {
290 : throw AipsError(
291 0 : preamble + "Beginning freq units (" + beginFreq.getUnit()
292 0 : + ") do not conform to ending freq units (" + endFreq.getUnit()
293 0 : + ") but they must."
294 0 : );
295 : }
296 :
297 0 : if (
298 0 : ! beginFreq.isConform("Hz")
299 0 : && ! beginFreq.isConform("m/s")
300 0 : && ! beginFreq.isConform("pix")
301 : ) {
302 : throw AipsError(
303 : preamble
304 0 : + "Invalid frequency unit " + beginFreq.getUnit()
305 0 : );
306 : }
307 0 : if (beginFreq.isConform("m/s") && restfreq.getValue() <= 0) {
308 : throw AipsError(
309 : preamble
310 0 : + "Beginning and ending velocities supplied but no restfreq specified"
311 0 : );
312 : }
313 0 : if (freqRefFrame.empty()) {
314 0 : _freqRefFrame = getCsys().spectralCoordinate().frequencySystem();
315 : }
316 0 : else if (! MFrequency::getType(_freqRefFrame, freqRefFrame)) {
317 : throw AipsError(
318 : preamble
319 0 : + "Unknown frequency frame code "
320 0 : + freqRefFrame
321 0 : );
322 : }
323 : else {
324 0 : _setParam(AnnotationBase::FRAME, freqRefFrame);
325 : }
326 0 : if (dopplerString.empty()) {
327 0 : _dopplerType = getCsys().spectralCoordinate().velocityDoppler();
328 : }
329 0 : else if (! MDoppler::getType(_dopplerType, dopplerString)) {
330 : throw AipsError(
331 0 : preamble + "Unknown doppler code " + dopplerString
332 0 : );
333 : }
334 : else {
335 0 : _setParam(AnnotationBase::VELTYPE, dopplerString);
336 : }
337 0 : _beginFreq = beginFreq;
338 0 : _endFreq = endFreq;
339 0 : _restFreq = restfreq;
340 0 : _setParam(AnnotationBase::RANGE, _printFreqRange());
341 0 : _setParam(AnnotationBase::RESTFREQ, _printFreq(_restFreq));
342 :
343 0 : _checkAndConvertFrequencies();
344 0 : return true;
345 : }
346 0 : return false;
347 : }
348 :
349 :
350 0 : Vector<MFrequency> AnnotationBase::getFrequencyLimits() const {
351 0 : return _convertedFreqLimits;
352 : }
353 :
354 0 : Vector<Stokes::StokesTypes> AnnotationBase::getStokes() const {
355 0 : return _stokes;
356 : }
357 :
358 0 : void AnnotationBase::_checkAndConvertFrequencies() {
359 0 : const CoordinateSystem& csys = getCsys();
360 0 : const SpectralCoordinate spcoord = csys.spectralCoordinate();
361 0 : MFrequency::Types cFrameType = spcoord.frequencySystem(false);
362 0 : MDoppler::Types cDopplerType = spcoord.velocityDoppler();
363 0 : _convertedFreqLimits.resize(2);
364 0 : for (Int i=0; i<2; i++) {
365 0 : Quantity qFreq = i == 0 ? _beginFreq : _endFreq;
366 0 : String unit = qFreq.getUnit();
367 0 : if (qFreq.isConform("pix")) {
368 0 : Int spectralAxisNumber = csys.spectralAxisNumber(true);
369 0 : String unit = csys.worldAxisUnits()[spectralAxisNumber];
370 : Double world;
371 0 : if (! spcoord.toWorld(world, qFreq.getValue())) {
372 0 : ostringstream os;
373 0 : os << String(__FUNCTION__) << ": Unable to convert pixel to world value "
374 0 : << "for spectral coordinate";
375 0 : throw AipsError(os.str());
376 : }
377 0 : if (_freqRefFrame != cFrameType) {
378 0 : LogIO log;
379 0 : log << LogOrigin(String(__FUNCTION__)) << LogIO::WARN
380 : << ": Frequency range given in pixels but supplied frequency ref frame ("
381 : << MFrequency::showType(_freqRefFrame) << ") differs from that of "
382 : << "the provided coordinate system (" << MFrequency::showType(cFrameType)
383 : << "). The provided frequency range will therefore be assumed to already "
384 : << "be in the coordinate system frequency reference frame and no conversion "
385 0 : << "will be done" << LogIO::POST;
386 : }
387 0 : if (_dopplerType != cDopplerType) {
388 0 : LogIO log;
389 0 : log << LogOrigin(String(__FUNCTION__)) << LogIO::WARN
390 : << ": Frequency range given in pixels but supplied doppler type ("
391 : << MDoppler::showType(_dopplerType) << ") differs from that of "
392 : << "the provided coordinate system (" << MDoppler::showType(cDopplerType)
393 : << "). The provided frequency range will therefore be assumed to already "
394 : << "be in the coordinate system doppler and no conversion "
395 0 : << "will be done" << LogIO::POST;
396 : }
397 0 : _freqRefFrame = cFrameType;
398 0 : _dopplerType = cDopplerType;
399 0 : _convertedFreqLimits[i] = MFrequency(
400 0 : Quantity(world, unit),
401 : _freqRefFrame
402 0 : );
403 : }
404 0 : else if (qFreq.isConform("m/s")) {
405 0 : MFrequency::Ref freqRef(_freqRefFrame);
406 0 : MDoppler::Ref velRef(_dopplerType);
407 0 : VelocityMachine vm(freqRef, Unit("GHz"),
408 0 : MVFrequency(_restFreq),
409 : velRef, unit
410 0 : );
411 0 : qFreq = vm(qFreq);
412 0 : _convertedFreqLimits[i] = MFrequency(qFreq, _freqRefFrame);
413 0 : if (_dopplerType != cDopplerType) {
414 0 : MDoppler dopplerConversion = MDoppler::Convert(_dopplerType, cDopplerType)();
415 0 : _convertedFreqLimits[i] = MFrequency::fromDoppler(
416 : dopplerConversion,
417 0 : _convertedFreqLimits[i].get("Hz"), cFrameType
418 0 : );
419 : }
420 : }
421 0 : else if ( qFreq.isConform("Hz")) {
422 0 : _convertedFreqLimits[i] = MFrequency(qFreq, _freqRefFrame);
423 : }
424 : else {
425 : throw AipsError("Logic error. Bad spectral unit "
426 0 : + unit
427 0 : + " somehow made it to a place where it shouldn't have"
428 0 : );
429 : }
430 0 : if (_freqRefFrame != cFrameType) {
431 0 : Vector<Double> refDirection = csys.directionCoordinate().referenceValue();
432 0 : Vector<String> directionUnits = csys.directionCoordinate().worldAxisUnits();
433 : MDirection refDir(
434 0 : Quantity(refDirection[0], directionUnits[0]),
435 0 : Quantity(refDirection[1], directionUnits[1]),
436 0 : getCsys().directionCoordinate().directionType()
437 0 : );
438 0 : MFrequency::Ref inFrame(_freqRefFrame, MeasFrame(refDir));
439 0 : MFrequency::Ref outFrame(cFrameType, MeasFrame(refDir));
440 0 : MFrequency::Convert converter(inFrame, outFrame);
441 0 : _convertedFreqLimits[i] = converter(_convertedFreqLimits[i]);
442 : }
443 : }
444 0 : }
445 :
446 :
447 0 : String AnnotationBase::_printFreqRange() const {
448 0 : ostringstream os;
449 0 : os << "["
450 0 : << _printFreq(_beginFreq) << ", "
451 0 : << _printFreq(_endFreq) << "]";
452 0 : return os.str();
453 : }
454 :
455 0 : String AnnotationBase::_printFreq(const Quantity& freq) {
456 0 : if (freq.isConform("pix")) {
457 0 : return _printPixel(freq.getValue());
458 : }
459 0 : ostringstream os;
460 0 : os << std::fixed;
461 0 : if (freq.isConform("km/s")) {
462 0 : os << std::setprecision(4) << freq.getValue("km/s") << "km/s";
463 : }
464 : else {
465 0 : os << std::setprecision(3) << freq.getValue("MHz") << "MHz";
466 : }
467 0 : return os.str();
468 : }
469 :
470 0 : AnnotationBase::Type AnnotationBase::getType() const {
471 0 : return _type;
472 : }
473 :
474 0 : void AnnotationBase::_initTypeMap() {
475 0 : _typeMap["line"] = LINE;
476 0 : _typeMap["vector"] = VECTOR;
477 0 : _typeMap["text"] = TEXT;
478 0 : _typeMap["symbol"] = SYMBOL;
479 0 : _typeMap["box"] = RECT_BOX;
480 0 : _typeMap["rectangularbox"] = RECT_BOX;
481 0 : _typeMap["centerbox"] = CENTER_BOX;
482 0 : _typeMap["rotatedbox"] = ROTATED_BOX;
483 0 : _typeMap["rotbox"] = ROTATED_BOX;
484 0 : _typeMap["poly"] = POLYGON;
485 0 : _typeMap["polygon"] = POLYGON;
486 0 : _typeMap["circle"] = CIRCLE;
487 0 : _typeMap["annulus"] = ANNULUS;
488 0 : _typeMap["ellipse"] = ELLIPSE;
489 0 : }
490 :
491 0 : AnnotationBase::Type AnnotationBase::typeFromString(
492 : const String& type
493 : ) {
494 0 : if (_typeMap.size() == 0) {
495 0 : _initTypeMap();
496 : }
497 0 : String cType = type;
498 0 : cType.downcase();
499 0 : cType.trim();
500 0 : if (_typeMap.find(cType) == _typeMap.end()) {
501 0 : throw AipsError(type + " is not a supported annotation type");
502 : }
503 0 : return _typeMap.at(cType);
504 : }
505 :
506 0 : String AnnotationBase::typeToString(const AnnotationBase::Type type) {
507 0 : if (_typeMap.size() == 0) {
508 0 : _initTypeMap();
509 : }
510 0 : for (
511 0 : map<String, Type>::const_iterator iter = _typeMap.begin();
512 0 : iter != _typeMap.end(); iter++
513 : ) {
514 0 : if (iter->second == type) {
515 0 : return iter->first;
516 : }
517 :
518 : }
519 : throw AipsError(
520 0 : _class + "::" + __FUNCTION__ + ": Logic error. Type "
521 0 : + String::toString(type) + " not handled"
522 0 : );
523 : }
524 :
525 :
526 0 : AnnotationBase::LineStyle AnnotationBase::lineStyleFromString(const String& ls) {
527 0 : if (_lineStyleMap.size() == 0) {
528 0 : _lineStyleMap["-"] = SOLID;
529 0 : _lineStyleMap["--"] = DASHED;
530 0 : _lineStyleMap["-."] = DOT_DASHED;
531 0 : _lineStyleMap[":"] = DOTTED;
532 : }
533 0 : String cls = ls;
534 0 : cls.trim();
535 0 : if (cls.empty()) {
536 0 : return DEFAULT_LINESTYLE;
537 : }
538 0 : if (_lineStyleMap.find(cls) == _lineStyleMap.end()) {
539 : throw AipsError(
540 0 : ls + " is not a supported line style"
541 0 : );
542 : }
543 0 : return _lineStyleMap.at(cls);
544 : }
545 :
546 : AnnotationBase::FontStyle
547 0 : AnnotationBase::fontStyleFromString(const String& fs) {
548 0 : String cfs = fs;
549 0 : cfs.downcase();
550 0 : cfs.trim();
551 : // FIXME when nothing to do and feeling anal, turn this into
552 : // a static map
553 0 : if (cfs.empty()) {
554 0 : return DEFAULT_FONTSTYLE;
555 : }
556 0 : else if (cfs == "normal") {
557 0 : return NORMAL;
558 : }
559 0 : else if (cfs == "bold") {
560 0 : return BOLD;
561 : }
562 0 : else if (cfs == "italic") {
563 0 : return ITALIC;
564 : }
565 0 : else if (cfs == "bold-italic") {
566 0 : return ITALIC_BOLD;
567 : }
568 : else {
569 : throw AipsError(
570 0 : fs + " is not a supported font style"
571 0 : );
572 : }
573 : }
574 :
575 0 : String AnnotationBase::fontStyleToString(
576 : const AnnotationBase::FontStyle fs
577 : ) {
578 0 : switch (fs) {
579 0 : case NORMAL: return "normal";
580 0 : case BOLD: return "bold";
581 0 : case ITALIC: return "italic";
582 0 : case ITALIC_BOLD: return "itatlic_bold";
583 0 : default:
584 : throw AipsError(
585 0 : _class + ": " + String(__FUNCTION__) + ": "
586 0 : + ": Logic error, should never have gotten here"
587 0 : );
588 : }
589 : }
590 :
591 0 : void AnnotationBase::setLabel(const String& s) {
592 0 : _label = s;
593 0 : if (_label.empty()) {
594 0 : if (_params.find(LABEL) != _params.end()) {
595 0 : _params.erase(LABEL);
596 : }
597 : }
598 : else {
599 0 : _params[LABEL] = _label;
600 : }
601 0 : }
602 :
603 0 : String AnnotationBase::getLabel() const {
604 0 : return _label;
605 : }
606 :
607 0 : Bool AnnotationBase::_isRGB(const AnnotationBase::RGB& rgb) {
608 0 : if (rgb.size() != 3) {
609 0 : return false;
610 : }
611 0 : for (RGB::const_iterator iter=rgb.begin(); iter!=rgb.end(); iter++) {
612 0 : if (*iter < 0 || *iter > 255) {
613 0 : return false;
614 : }
615 : }
616 0 : return true;
617 : }
618 :
619 0 : AnnotationBase::RGB AnnotationBase::_colorStringToRGB(const String& s) {
620 0 : String c = s;
621 0 : c.trim();
622 0 : c.downcase();
623 0 : if (_colors.find(c) != _colors.end()) {
624 0 : return _colors.find(c)->second;
625 : }
626 0 : else if (c.find(rgbHexRegex) != String::npos) {
627 0 : RGB rgb(3);
628 0 : for (uInt i=0; i<3; i++) {
629 0 : String comp = s.substr(2*i, 2);
630 : int hexInt;
631 0 : sscanf(comp.c_str(), "%x", &hexInt );
632 0 : rgb[i] = hexInt;
633 : }
634 0 : return rgb;
635 : }
636 : else {
637 0 : throw AipsError("Unrecognized color specification " + s);
638 : }
639 : }
640 :
641 0 : void AnnotationBase::setColor(const String& s) {
642 0 : _color = _colorStringToRGB(s);
643 0 : _params[COLOR] = colorToString(_color);
644 0 : }
645 :
646 0 : void AnnotationBase::setColor(const RGB& rgb) {
647 0 : if (! _isRGB(rgb)) {
648 : throw AipsError(
649 0 : _class + "::" + __FUNCTION__
650 0 : + ": input vector is not a valid RGB representation"
651 0 : );
652 : }
653 0 : _color = rgb;
654 0 : _params[COLOR] = colorToString(_color);
655 0 : }
656 :
657 0 : AnnotationBase::RGB AnnotationBase::getColor() const {
658 0 : return _color;
659 : }
660 :
661 0 : String AnnotationBase::getColorString() const {
662 0 : return colorToString(_color);
663 : }
664 :
665 0 : String AnnotationBase::colorToString(const AnnotationBase::RGB& color) {
666 0 : if (! _isRGB(color)) {
667 : throw AipsError(
668 0 : _class + "::" + __FUNCTION__
669 0 : + ": input vector is not a valid RGB representation"
670 0 : );
671 : }
672 0 : if (_rgbNameMap.find(color) != _rgbNameMap.end()) {
673 0 : return _rgbNameMap.find(color)->second;
674 : }
675 : else {
676 0 : ostringstream oss;
677 0 : oss << hex << std::setw(2) << std::setfill('0') << (Int)floor(color[0] + 0.5)
678 0 : << hex << std::setw(2) << std::setfill('0') << (Int)floor(color[1] + 0.5)
679 0 : << hex << std::setw(2) << std::setfill('0') << (Int)floor(color[2] + 0.5);
680 0 : String rgbString = oss.str();
681 0 : rgbString.downcase();
682 0 : return rgbString;
683 : }
684 : }
685 :
686 0 : void AnnotationBase::setLabelColor(const String& color) {
687 0 : _labelColor = _colorStringToRGB(color);
688 0 : _params[LABELCOLOR] = color;
689 0 : }
690 :
691 0 : void AnnotationBase::setLabelColor(const RGB& color) {
692 0 : if (! _isRGB(color)) {
693 : throw AipsError(
694 0 : _class + "::" + __FUNCTION__
695 0 : + ": input vector is not a valid RGB representation"
696 0 : );
697 : }
698 0 : _labelColor = color;
699 0 : _params[LABELCOLOR] = colorToString(_labelColor);
700 0 : }
701 :
702 0 : String AnnotationBase::getLabelColorString() const {
703 0 : return colorToString(_labelColor);
704 : }
705 :
706 0 : AnnotationBase::RGB AnnotationBase::getLabelColor() const {
707 0 : return _labelColor;
708 : }
709 :
710 0 : void AnnotationBase::setLineStyle(const LineStyle s) {
711 0 : _linestyle = s;
712 0 : _params[LINESTYLE] = lineStyleToString(_linestyle);
713 0 : }
714 :
715 0 : AnnotationBase::LineStyle AnnotationBase::getLineStyle() const {
716 0 : return _linestyle;
717 : }
718 :
719 0 : void AnnotationBase::setLineWidth(const uInt s) {
720 0 : _linewidth = s;
721 0 : _params[LINEWIDTH] = String::toString(_linewidth);
722 0 : }
723 :
724 0 : uInt AnnotationBase::getLineWidth() const {
725 0 : return _linewidth;
726 : }
727 :
728 0 : void AnnotationBase::setSymbolSize(const uInt s) {
729 0 : _symbolsize = s;
730 0 : _params[SYMSIZE] = String::toString(_symbolsize);
731 0 : }
732 :
733 0 : uInt AnnotationBase::getSymbolSize() const {
734 0 : return _symbolsize;
735 : }
736 :
737 0 : void AnnotationBase::setSymbolThickness(const uInt s) {
738 0 : _symbolthickness = s;
739 0 : _params[SYMTHICK] = String::toString(_symbolthickness);
740 0 : }
741 :
742 0 : uInt AnnotationBase::getSymbolThickness() const {
743 0 : return _symbolthickness;
744 : }
745 :
746 0 : void AnnotationBase::setFont(const String& s) {
747 0 : _font = s;
748 0 : _params[FONT] = _font;
749 0 : }
750 :
751 0 : String AnnotationBase::getFont() const {
752 0 : return _font;
753 : }
754 :
755 0 : void AnnotationBase::setFontSize(const uInt s) {
756 0 : _fontsize = s;
757 0 : _params[FONTSIZE] = String::toString(_fontsize);
758 0 : }
759 :
760 0 : uInt AnnotationBase::getFontSize() const {
761 0 : return _fontsize;
762 : }
763 :
764 0 : void AnnotationBase::setFontStyle(const AnnotationBase::FontStyle& fs) {
765 0 : _fontstyle = fs;
766 0 : _params[FONTSTYLE] = fontStyleToString(_fontstyle);
767 0 : }
768 :
769 0 : AnnotationBase::FontStyle AnnotationBase::getFontStyle() const {
770 0 : return _fontstyle;
771 : }
772 :
773 0 : void AnnotationBase::setUseTex(const Bool s) {
774 0 : _usetex = s;
775 0 : _params[USETEX] = _usetex ? "true" : "false";
776 0 : }
777 :
778 0 : Bool AnnotationBase::isUseTex() const {
779 0 : return _usetex;
780 : }
781 :
782 0 : String AnnotationBase::getLabelPosition() const {
783 0 : return _labelPos;
784 : }
785 :
786 0 : void AnnotationBase::setLabelPosition(const String& position) {
787 0 : String c = position;
788 0 : c.trim();
789 0 : c.downcase();
790 0 : if (
791 0 : c != "top" && c != "bottom"
792 0 : && c != "left" && c != "right"
793 : ) {
794 : throw AipsError(
795 0 : _class + "::" + __FUNCTION__
796 0 : + ": Unknown label position " + position
797 0 : );
798 : }
799 0 : _labelPos = c;
800 0 : _params[LABELPOS] = _labelPos;
801 0 : }
802 :
803 0 : void AnnotationBase::setLabelOffset(const vector<Int>& offset) {
804 0 : if (offset.size() != 2) {
805 : throw AipsError(
806 0 : _class + "::" + __FUNCTION__
807 0 : + ": Number of elements in label offset must be exactly 2, not "
808 0 : + String(offset.size())
809 0 : );
810 : }
811 0 : _labelOff = offset;
812 0 : _params[LABELOFF] = "[" + String::toString(offset[0]) + ", " + String::toString(offset[1]) + "]";
813 0 : }
814 :
815 0 : vector<Int> AnnotationBase::getLabelOffset() const {
816 0 : return _labelOff;
817 : }
818 :
819 0 : Bool AnnotationBase::isRegion() const {
820 0 : return false;
821 : }
822 :
823 0 : void AnnotationBase::setGlobals(
824 : const Vector<Keyword>& globalKeys
825 : ) {
826 0 : for (
827 0 : Vector<Keyword>::const_iterator iter=globalKeys.begin();
828 0 : iter != globalKeys.end(); iter++) {
829 0 : _globals[*iter] = true;
830 : }
831 0 : }
832 :
833 0 : String AnnotationBase::keywordToString(
834 : const Keyword key
835 : ) {
836 0 : switch(key) {
837 0 : case COORD: return "coord";
838 0 : case RANGE: return "range";
839 0 : case FRAME: return "frame";
840 0 : case CORR: return "corr";
841 0 : case VELTYPE: return "veltype";
842 0 : case RESTFREQ: return "restfreq";
843 0 : case LINEWIDTH: return "linewidth";
844 0 : case LINESTYLE: return "linestyle";
845 0 : case SYMSIZE: return "symsize";
846 0 : case SYMTHICK: return "symthick";
847 0 : case COLOR: return "color";
848 0 : case FONT: return "font";
849 0 : case FONTSIZE: return "fontsize";
850 0 : case FONTSTYLE: return "fontstyle";
851 0 : case USETEX: return "usetex";
852 0 : case LABEL: return "label";
853 0 : case LABELCOLOR: return "labelcolor";
854 0 : case LABELPOS: return "labelpos";
855 0 : case LABELOFF: return "labeloff";
856 0 : case UNKNOWN_KEYWORD:
857 : case N_KEYS:
858 : default:
859 : throw AipsError(
860 0 : _class + "::" + __FUNCTION__
861 0 : + ": Logic error: No string representation for Keyword " + String(key)
862 0 : );
863 : }
864 : }
865 :
866 0 : String AnnotationBase::lineStyleToString(
867 : const LineStyle style
868 : ) {
869 0 : switch(style) {
870 0 : case SOLID: return "-";
871 0 : case DASHED: return "--";
872 0 : case DOT_DASHED: return "-.";
873 0 : case DOTTED: return ":";
874 0 : default:
875 0 : ThrowCc(
876 : "Logic error: No string representation for LineStyle "
877 : + String::toString(style)
878 : );
879 : }
880 : }
881 :
882 0 : ostream& AnnotationBase::print(
883 : ostream& os, const LineStyle ls
884 : ) {
885 0 : os << lineStyleToString(ls);
886 0 : return os;
887 : }
888 :
889 0 : ostream& AnnotationBase::print(
890 : ostream& os, const FontStyle fs
891 : ) {
892 0 : os << fontStyleToString(fs);
893 0 : return os;
894 : }
895 :
896 0 : ostream& AnnotationBase::print(
897 : ostream& os, const map<Keyword, String>& params
898 : ) {
899 0 : if (params.size() == 0) {
900 0 : return os;
901 : }
902 0 : Bool hasLabel = params.find(LABEL) != params.end();
903 0 : for (
904 0 : map<Keyword, String>::const_iterator iter=params.begin();
905 0 : iter!=params.end(); iter++
906 : ) {
907 0 : Keyword key = iter->first;
908 0 : if (! iter->second.empty()) {
909 0 : if (
910 0 : ! hasLabel && (
911 0 : key == LABELCOLOR || key == LABELPOS
912 0 : || key == LABELOFF
913 : )
914 : ) {
915 0 : continue;
916 : }
917 0 : if (iter != params.begin()) {
918 0 : os << ", ";
919 : }
920 : String quote = key == LABEL
921 0 : || (
922 0 : iter->second.contains(' ')
923 0 : && (key != RANGE && key != CORR && key != LABELOFF)
924 : )
925 0 : ? "\"" : "";
926 0 : os << keywordToString((Keyword)iter->first)
927 0 : << "=" << quote << iter->second << quote;
928 : }
929 : }
930 0 : return os;
931 : }
932 :
933 0 : ostream& AnnotationBase::print(
934 : ostream& os, const Direction d
935 : ) {
936 0 : for (uInt i=0; i<d.size(); i++) {
937 0 : os << i << ": " << d[i].first << ", " << d[i].second << endl;
938 : }
939 0 : return os;
940 : }
941 :
942 0 : void AnnotationBase::_printPairs(ostream &os) const {
943 0 : map<Keyword, String> x = _params;
944 0 : if (! _printGlobals) {
945 0 : for (
946 0 : map<Keyword, String>::const_iterator iter = _params.begin();
947 0 : iter != _params.end(); iter++
948 : ) {
949 0 : Keyword k = iter->first;
950 0 : if (_globals.find(k) != _globals.end() && _globals.at(k)) {
951 0 : x.erase(k);
952 : }
953 : }
954 : }
955 0 : if (x.size() > 0) {
956 0 : os << " " << x;
957 : }
958 0 : }
959 :
960 0 : void AnnotationBase::_checkMixed(
961 : const String& origin, const AnnotationBase::Direction& quantities
962 : ) {
963 0 : Bool isWorld = false;
964 0 : Bool isPixel = false;
965 0 : Quantity qArg;
966 0 : for (
967 0 : Direction::const_iterator iter = quantities.begin();
968 0 : iter != quantities.end(); iter++
969 : ) {
970 0 : for (uInt i=0; i<2; i++) {
971 0 : Quantity tQ = i == 0 ? iter->first : iter->second;
972 0 : Bool pix = tQ.getUnit() == "pix";
973 0 : Bool world = ! pix;
974 0 : isWorld = isWorld || world;
975 0 : isPixel = isPixel || pix;
976 0 : if (isPixel && isWorld) {
977 : throw AipsError(
978 : origin
979 0 : + ": Mixed world and pixel coordinates not supported"
980 0 : );
981 : }
982 : }
983 : }
984 0 : }
985 :
986 0 : MDirection AnnotationBase::_directionFromQuantities(
987 : const Quantity& q0, const Quantity& q1
988 : ) {
989 0 : ostringstream oss;
990 0 : oss << q0 << ", " << q1;
991 0 : Quantity d0 = q0;
992 0 : Quantity d1 = q1;
993 :
994 0 : String value = oss.str();
995 0 : if (q0.getUnit() == "pix") {
996 : // both quantities are in pix, this check should
997 : // have been done prior to calling this method
998 0 : Vector<Double> pixel(_csys.nPixelAxes(), 0);
999 0 : pixel[_directionAxes[0]] = q0.getValue();
1000 0 : pixel[_directionAxes[1]] = q1.getValue();
1001 0 : Vector<Double> world;
1002 0 : _csys.toWorld(world, pixel);
1003 0 : Vector<String> axesUnits = _csys.worldAxisUnits();
1004 0 : d0 = Quantity(world[_directionAxes[0]], axesUnits[_directionAxes[0]]);
1005 0 : d1 = Quantity(world[_directionAxes[1]], axesUnits[_directionAxes[1]]);
1006 0 : MDirection::Types csysDirectionType = _csys.directionCoordinate().directionType(false);
1007 0 : if (_directionRefFrame != csysDirectionType) {
1008 0 : LogIO log;
1009 0 : log << LogOrigin(String(__FUNCTION__)) << LogIO::WARN
1010 : << ": Direction quantities specified in pixels but specified direction reference "
1011 : << "frame (" << MDirection::showType(_directionRefFrame) << ") is different from "
1012 : << "the reference frame (" << MDirection::showType(csysDirectionType)
1013 : << ") of the coordinate system. The reference frame of the coordinate system "
1014 : << "will be used and the direction coordinates will not be transformed"
1015 0 : << LogIO::POST;
1016 : }
1017 0 : _directionRefFrame = csysDirectionType;
1018 : }
1019 : try {
1020 0 : return MDirection(d0, d1, _directionRefFrame);
1021 : }
1022 0 : catch (const AipsError& x) {
1023 : throw AipsError(
1024 0 : _class + "::" + String(__FUNCTION__) + ": Error converting direction ("
1025 0 : + value + ") to MDirection: " + x.getMesg()
1026 0 : );
1027 : }
1028 : }
1029 :
1030 0 : void AnnotationBase::_checkAndConvertDirections(
1031 : const String& origin, const AnnotationBase::Direction& quantities
1032 : ) {
1033 0 : _checkMixed(origin, quantities);
1034 0 : MDirection::Types csysDirectionRefFrame = _csys.directionCoordinate().directionType(false);
1035 0 : Bool needsConverting = _directionRefFrame != csysDirectionRefFrame;
1036 0 : _convertedDirections.resize(quantities.size());
1037 0 : for (uInt i=0; i<quantities.size(); i++) {
1038 0 : _convertedDirections[i] = _directionFromQuantities(quantities(i).first, quantities(i).second);
1039 0 : if (needsConverting) {
1040 0 : _convertedDirections[i] = MDirection::Convert(_convertedDirections[i], csysDirectionRefFrame)();
1041 : }
1042 : }
1043 : // check this now because if converting from world to pixel fails when
1044 : // regions are being formed, it will wreak havoc
1045 0 : _testConvertToPixel();
1046 0 : }
1047 :
1048 0 : AnnotationBase::Direction AnnotationBase::getDirections() const {
1049 0 : Direction res(_convertedDirections.size());
1050 0 : for (uInt i=0; i<res.size(); i++) {
1051 0 : Quantum<Vector<Double> > angles = _convertedDirections[i].getAngle();
1052 0 : String unit = angles.getUnit();
1053 0 : Vector<Double> vals = angles.getValue();
1054 0 : res[i].first = Quantity(vals[0], unit);
1055 0 : res[i].second = Quantity(vals[1], unit);
1056 :
1057 : }
1058 0 : return res;
1059 : }
1060 :
1061 0 : void AnnotationBase::_initColors() {
1062 0 : if (_doneColorInit) {
1063 0 : return;
1064 : }
1065 0 : _colors.insert(make_pair("black", BLACK));
1066 0 : _colors.insert(make_pair("blue", BLUE));
1067 0 : _colors.insert(make_pair("cyan", CYAN));
1068 0 : _colors.insert(make_pair("gray", GRAY));
1069 0 : _colors.insert(make_pair("green", GREEN));
1070 0 : _colors.insert(make_pair("magenta", MAGENTA));
1071 0 : _colors.insert(make_pair("orange", ORANGE));
1072 0 : _colors.insert(make_pair("red", RED));
1073 0 : _colors.insert(make_pair("white", WHITE));
1074 0 : _colors.insert(make_pair("yellow", YELLOW));
1075 :
1076 0 : for (
1077 0 : map<string, RGB>::const_iterator iter=_colors.begin();
1078 0 : iter != _colors.end(); iter++
1079 : ) {
1080 0 : _rgbNameMap[iter->second] = iter->first;
1081 0 : _colorNames.push_back(iter->first);
1082 : }
1083 0 : _doneColorInit = true;
1084 : }
1085 :
1086 0 : std::list<std::string> AnnotationBase::colorChoices() {
1087 0 : _initColors();
1088 0 : return _colorNames;
1089 : }
1090 :
1091 0 : void AnnotationBase::_testConvertToPixel() const {
1092 0 : Vector<Double> pixel(2);
1093 0 : Vector<Double> world(2);
1094 0 : const auto units = _csys.worldAxisUnits();
1095 0 : const auto end = _convertedDirections.end();
1096 0 : for (auto iter = _convertedDirections.begin(); iter != end; ++iter) {
1097 0 : world[0] = iter->getAngle().getValue(units[0])[0];
1098 0 : world[1] = iter->getAngle().getValue(units[1])[1];
1099 0 : if (! _csys.directionCoordinate().toPixel(pixel, world)) {
1100 0 : ostringstream oss;
1101 0 : oss << "Could not convert world coordinate " << world << "to pixel";
1102 0 : throw (WorldToPixelConversionError(oss.str()));
1103 : }
1104 : }
1105 0 : }
1106 :
1107 0 : String AnnotationBase::_printDirection(
1108 : const Quantity& longitude, const Quantity& latitude
1109 : ) const {
1110 0 : if (longitude.getUnit() == "pix") {
1111 0 : ostringstream os;
1112 0 : os << _printPixel(longitude.getValue())
1113 0 : << ", "
1114 0 : << _printPixel(latitude.getValue());
1115 0 : return os.str();
1116 : }
1117 : MDirection::Types frame;
1118 0 : MDirection::getType(frame, _params.find(COORD)->second);
1119 0 : if (
1120 0 : frame == MDirection::J2000
1121 0 : || frame == MDirection::B1950
1122 0 : || frame == MDirection::JMEAN
1123 0 : || frame == MDirection::JTRUE
1124 0 : || frame == MDirection::B1950_VLA
1125 0 : || frame == MDirection::BMEAN
1126 0 : || frame == MDirection::BTRUE
1127 : ) {
1128 : // equatorial coordinates in sexigesimal
1129 0 : MVAngle x(longitude);
1130 0 : MVAngle y(latitude);
1131 0 : return x.string(MVAngle::TIME_CLEAN, 11) + ", " + y.string(MVAngle::ANGLE, 10);
1132 : }
1133 : else {
1134 : // non-equatorial coordinates in degrees
1135 0 : return _toDeg(longitude) + ", " + _toDeg(latitude);
1136 : }
1137 : }
1138 :
1139 0 : String AnnotationBase::_toArcsec(const Quantity& angle) {
1140 0 : ostringstream os;
1141 0 : if (angle.getUnit() == "pix") {
1142 0 : os << _printPixel(angle.getValue());
1143 : }
1144 : else {
1145 0 : os << std::fixed << std::setprecision(4)
1146 0 : << angle.getValue("arcsec") << "arcsec";
1147 : }
1148 0 : return os.str();
1149 : }
1150 :
1151 0 : String AnnotationBase::_toDeg(const Quantity& angle) {
1152 0 : ostringstream os;
1153 0 : if (angle.getUnit() == "pix") {
1154 0 : os << _printPixel(angle.getValue());
1155 : }
1156 : else {
1157 0 : os << std::fixed << std::setprecision(8)
1158 0 : << angle.getValue("deg") << "deg";
1159 : }
1160 0 : return os.str();
1161 : }
1162 :
1163 0 : String AnnotationBase::_printPixel(const Double& d) {
1164 0 : ostringstream os;
1165 0 : os << std::fixed << std::setprecision(1)
1166 0 : << d << "pix";
1167 0 : return os.str();
1168 : }
1169 :
1170 :
1171 : }
1172 :
1173 :
1174 :
|