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 139 : 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 139 : )
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 139 : _printGlobals(false), _labelOff(DEFAULT_LABELOFF) {
103 139 : ThrowIf(
104 : ! csys.hasDirectionCoordinate(),
105 : "Coordinate system has no direction coordinate"
106 : );
107 : const uInt *oname;
108 : Int nall, nex;
109 139 : 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 139 : ThrowIf(
113 : find( tname, tname+nall, dirRefFrameString) == tname+nall,
114 : "Unknown direction reference frame '" + dirRefFrameString + "'"
115 : );
116 139 : ThrowIf (
117 : ! MDirection::getType(_directionRefFrame, dirRefFrameString),
118 : "Unknown direction reference frame " + dirRefFrameString
119 : );
120 139 : setFrequencyLimits(
121 : beginFreq, endFreq, freqRefFrame,
122 : dopplerString, restfreq
123 : );
124 139 : _init();
125 139 : }
126 :
127 427 : AnnotationBase::AnnotationBase(
128 : const Type type, const CoordinateSystem& csys,
129 : const Vector<Stokes::StokesTypes>& stokes
130 427 : )
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 427 : _printGlobals(false), _labelOff(DEFAULT_LABELOFF)
142 : {
143 854 : String preamble = String(__FUNCTION__) + ": ";
144 427 : if (!csys.hasDirectionCoordinate()) {
145 : throw AipsError(
146 0 : preamble + "Coordinate system has no direction coordinate"
147 0 : );
148 : }
149 427 : _directionRefFrame = _csys.directionCoordinate().directionType();
150 427 : _init();
151 427 : }
152 :
153 566 : 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 566 : void AnnotationBase::_init() {
191 1698 : String preamble = _class + ": " + String(__FUNCTION__) + ": ";
192 566 : _initColors();
193 566 : if (
194 566 : _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 566 : && _directionRefFrame != MDirection::ICRS
204 : ) {
205 : throw AipsError(preamble
206 0 : + "Unsupported coordinate frame for regions "
207 0 : + MDirection::showType(_directionRefFrame)
208 0 : );
209 : }
210 566 : _params[COORD] = MDirection::showType(_directionRefFrame);
211 566 : _directionAxes = IPosition(_csys.directionAxesNumbers());
212 :
213 566 : uInt nStokes = _stokes.size();
214 566 : if (nStokes > 0) {
215 3 : ostringstream os;
216 3 : os << "[";
217 7 : for (uInt i=0; i< nStokes; i++) {
218 4 : os << Stokes::name(_stokes[i]);
219 4 : if (i != _stokes.size() - 1) {
220 1 : os << ", ";
221 : }
222 : }
223 3 : os << "]";
224 3 : _params[CORR] = os.str();
225 : }
226 11886 : for(uInt i=0; i<N_KEYS; i++) {
227 11320 : _globals[(Keyword)i] = false;
228 : }
229 566 : _initParams();
230 566 : }
231 :
232 566 : void AnnotationBase::_initParams() {
233 566 : _params[LINEWIDTH] = String::toString(_linewidth);
234 566 : _params[LINESTYLE] = lineStyleToString(_linestyle);
235 566 : _params[SYMSIZE] = String::toString(_symbolsize);
236 566 : _params[SYMTHICK] = String::toString(_symbolthickness);
237 566 : _params[COLOR] = getColorString();
238 566 : _params[FONT] = _font;
239 566 : _params[FONTSIZE] = String::toString(_fontsize);
240 566 : _params[FONTSTYLE] = fontStyleToString(_fontstyle);
241 566 : _params[USETEX] = _usetex ? "true" : "false";
242 566 : if (! _label.empty()) {
243 0 : _params[LABEL] = _label;
244 : }
245 566 : }
246 :
247 68 : void AnnotationBase::unitInit() {
248 68 : if (! _doneUnitInit) {
249 1 : UnitMap::putUser("pix",UnitVal(1.0), "pixel units");
250 1 : UnitMap::putUser("channel",UnitVal(1.0), "channel number");
251 1 : UnitMap::putUser("chan",UnitVal(1.0), "channel number");
252 1 : _doneUnitInit = true;
253 : }
254 68 : }
255 :
256 :
257 139 : 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 417 : String preamble(_class + ": " + String(__FUNCTION__) + ": ");
265 139 : if (beginFreq.getValue() == 0 && endFreq.getValue() == 0) {
266 128 : return false;
267 : }
268 11 : if (! getCsys().hasSpectralAxis()) {
269 0 : return false;
270 : }
271 11 : if ( beginFreq.getUnit().empty() && endFreq.getUnit().empty()) {
272 : throw AipsError(
273 0 : preamble + "Neither frequency specified has units. Both must"
274 0 : );
275 : }
276 11 : 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 11 : 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 11 : if (! beginFreq.getUnit().empty()) {
289 11 : 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 11 : if (
298 11 : ! beginFreq.isConform("Hz")
299 13 : && ! beginFreq.isConform("m/s")
300 13 : && ! beginFreq.isConform("pix")
301 : ) {
302 : throw AipsError(
303 : preamble
304 0 : + "Invalid frequency unit " + beginFreq.getUnit()
305 0 : );
306 : }
307 11 : 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 11 : if (freqRefFrame.empty()) {
314 11 : _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 11 : if (dopplerString.empty()) {
327 11 : _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 11 : _beginFreq = beginFreq;
338 11 : _endFreq = endFreq;
339 11 : _restFreq = restfreq;
340 11 : _setParam(AnnotationBase::RANGE, _printFreqRange());
341 11 : _setParam(AnnotationBase::RESTFREQ, _printFreq(_restFreq));
342 :
343 11 : _checkAndConvertFrequencies();
344 11 : return true;
345 : }
346 0 : return false;
347 : }
348 :
349 :
350 566 : Vector<MFrequency> AnnotationBase::getFrequencyLimits() const {
351 566 : return _convertedFreqLimits;
352 : }
353 :
354 566 : Vector<Stokes::StokesTypes> AnnotationBase::getStokes() const {
355 566 : return _stokes;
356 : }
357 :
358 11 : void AnnotationBase::_checkAndConvertFrequencies() {
359 11 : const CoordinateSystem& csys = getCsys();
360 22 : const SpectralCoordinate spcoord = csys.spectralCoordinate();
361 11 : MFrequency::Types cFrameType = spcoord.frequencySystem(false);
362 11 : MDoppler::Types cDopplerType = spcoord.velocityDoppler();
363 11 : _convertedFreqLimits.resize(2);
364 33 : for (Int i=0; i<2; i++) {
365 44 : Quantity qFreq = i == 0 ? _beginFreq : _endFreq;
366 44 : String unit = qFreq.getUnit();
367 22 : if (qFreq.isConform("pix")) {
368 4 : Int spectralAxisNumber = csys.spectralAxisNumber(true);
369 4 : String unit = csys.worldAxisUnits()[spectralAxisNumber];
370 : Double world;
371 4 : 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 4 : 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 4 : 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 4 : _freqRefFrame = cFrameType;
398 4 : _dopplerType = cDopplerType;
399 8 : _convertedFreqLimits[i] = MFrequency(
400 8 : Quantity(world, unit),
401 : _freqRefFrame
402 4 : );
403 : }
404 18 : 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 18 : else if ( qFreq.isConform("Hz")) {
422 18 : _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 22 : 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 11 : }
445 :
446 :
447 11 : String AnnotationBase::_printFreqRange() const {
448 11 : ostringstream os;
449 11 : os << "["
450 11 : << _printFreq(_beginFreq) << ", "
451 11 : << _printFreq(_endFreq) << "]";
452 22 : return os.str();
453 : }
454 :
455 33 : String AnnotationBase::_printFreq(const Quantity& freq) {
456 33 : if (freq.isConform("pix")) {
457 4 : return _printPixel(freq.getValue());
458 : }
459 29 : ostringstream os;
460 29 : os << std::fixed;
461 29 : if (freq.isConform("km/s")) {
462 0 : os << std::setprecision(4) << freq.getValue("km/s") << "km/s";
463 : }
464 : else {
465 29 : os << std::setprecision(3) << freq.getValue("MHz") << "MHz";
466 : }
467 29 : return os.str();
468 : }
469 :
470 0 : AnnotationBase::Type AnnotationBase::getType() const {
471 0 : return _type;
472 : }
473 :
474 1 : void AnnotationBase::_initTypeMap() {
475 1 : _typeMap["line"] = LINE;
476 1 : _typeMap["vector"] = VECTOR;
477 1 : _typeMap["text"] = TEXT;
478 1 : _typeMap["symbol"] = SYMBOL;
479 1 : _typeMap["box"] = RECT_BOX;
480 1 : _typeMap["rectangularbox"] = RECT_BOX;
481 1 : _typeMap["centerbox"] = CENTER_BOX;
482 1 : _typeMap["rotatedbox"] = ROTATED_BOX;
483 1 : _typeMap["rotbox"] = ROTATED_BOX;
484 1 : _typeMap["poly"] = POLYGON;
485 1 : _typeMap["polygon"] = POLYGON;
486 1 : _typeMap["circle"] = CIRCLE;
487 1 : _typeMap["annulus"] = ANNULUS;
488 1 : _typeMap["ellipse"] = ELLIPSE;
489 1 : }
490 :
491 139 : AnnotationBase::Type AnnotationBase::typeFromString(
492 : const String& type
493 : ) {
494 139 : if (_typeMap.size() == 0) {
495 1 : _initTypeMap();
496 : }
497 139 : String cType = type;
498 139 : cType.downcase();
499 139 : cType.trim();
500 139 : if (_typeMap.find(cType) == _typeMap.end()) {
501 0 : throw AipsError(type + " is not a supported annotation type");
502 : }
503 278 : 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 141 : AnnotationBase::LineStyle AnnotationBase::lineStyleFromString(const String& ls) {
527 141 : if (_lineStyleMap.size() == 0) {
528 1 : _lineStyleMap["-"] = SOLID;
529 1 : _lineStyleMap["--"] = DASHED;
530 1 : _lineStyleMap["-."] = DOT_DASHED;
531 1 : _lineStyleMap[":"] = DOTTED;
532 : }
533 282 : String cls = ls;
534 141 : cls.trim();
535 141 : if (cls.empty()) {
536 137 : return DEFAULT_LINESTYLE;
537 : }
538 4 : if (_lineStyleMap.find(cls) == _lineStyleMap.end()) {
539 : throw AipsError(
540 0 : ls + " is not a supported line style"
541 0 : );
542 : }
543 4 : return _lineStyleMap.at(cls);
544 : }
545 :
546 : AnnotationBase::FontStyle
547 141 : AnnotationBase::fontStyleFromString(const String& fs) {
548 282 : String cfs = fs;
549 141 : cfs.downcase();
550 141 : cfs.trim();
551 : // FIXME when nothing to do and feeling anal, turn this into
552 : // a static map
553 141 : if (cfs.empty()) {
554 137 : return DEFAULT_FONTSTYLE;
555 : }
556 4 : else if (cfs == "normal") {
557 4 : 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 705 : String AnnotationBase::fontStyleToString(
576 : const AnnotationBase::FontStyle fs
577 : ) {
578 705 : switch (fs) {
579 2 : case NORMAL: return "normal";
580 703 : 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 2 : void AnnotationBase::setLabel(const String& s) {
592 2 : _label = s;
593 2 : if (_label.empty()) {
594 0 : if (_params.find(LABEL) != _params.end()) {
595 0 : _params.erase(LABEL);
596 : }
597 : }
598 : else {
599 2 : _params[LABEL] = _label;
600 : }
601 2 : }
602 :
603 0 : String AnnotationBase::getLabel() const {
604 0 : return _label;
605 : }
606 :
607 853 : Bool AnnotationBase::_isRGB(const AnnotationBase::RGB& rgb) {
608 853 : if (rgb.size() != 3) {
609 0 : return false;
610 : }
611 3412 : for (RGB::const_iterator iter=rgb.begin(); iter!=rgb.end(); iter++) {
612 2559 : if (*iter < 0 || *iter > 255) {
613 0 : return false;
614 : }
615 : }
616 853 : return true;
617 : }
618 :
619 143 : AnnotationBase::RGB AnnotationBase::_colorStringToRGB(const String& s) {
620 286 : String c = s;
621 143 : c.trim();
622 143 : c.downcase();
623 143 : if (_colors.find(c) != _colors.end()) {
624 142 : return _colors.find(c)->second;
625 : }
626 1 : else if (c.find(rgbHexRegex) != String::npos) {
627 2 : RGB rgb(3);
628 4 : for (uInt i=0; i<3; i++) {
629 3 : String comp = s.substr(2*i, 2);
630 : int hexInt;
631 3 : sscanf(comp.c_str(), "%x", &hexInt );
632 3 : rgb[i] = hexInt;
633 : }
634 1 : return rgb;
635 : }
636 : else {
637 0 : throw AipsError("Unrecognized color specification " + s);
638 : }
639 : }
640 :
641 139 : void AnnotationBase::setColor(const String& s) {
642 139 : _color = _colorStringToRGB(s);
643 139 : _params[COLOR] = colorToString(_color);
644 139 : }
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 566 : String AnnotationBase::getColorString() const {
662 566 : return colorToString(_color);
663 : }
664 :
665 853 : String AnnotationBase::colorToString(const AnnotationBase::RGB& color) {
666 853 : if (! _isRGB(color)) {
667 : throw AipsError(
668 0 : _class + "::" + __FUNCTION__
669 0 : + ": input vector is not a valid RGB representation"
670 0 : );
671 : }
672 853 : if (_rgbNameMap.find(color) != _rgbNameMap.end()) {
673 851 : return _rgbNameMap.find(color)->second;
674 : }
675 : else {
676 4 : ostringstream oss;
677 2 : oss << hex << std::setw(2) << std::setfill('0') << (Int)floor(color[0] + 0.5)
678 2 : << hex << std::setw(2) << std::setfill('0') << (Int)floor(color[1] + 0.5)
679 2 : << hex << std::setw(2) << std::setfill('0') << (Int)floor(color[2] + 0.5);
680 4 : String rgbString = oss.str();
681 2 : rgbString.downcase();
682 2 : return rgbString;
683 : }
684 : }
685 :
686 4 : void AnnotationBase::setLabelColor(const String& color) {
687 4 : _labelColor = _colorStringToRGB(color);
688 4 : _params[LABELCOLOR] = color;
689 4 : }
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 139 : void AnnotationBase::setLineStyle(const LineStyle s) {
711 139 : _linestyle = s;
712 139 : _params[LINESTYLE] = lineStyleToString(_linestyle);
713 139 : }
714 :
715 0 : AnnotationBase::LineStyle AnnotationBase::getLineStyle() const {
716 0 : return _linestyle;
717 : }
718 :
719 139 : void AnnotationBase::setLineWidth(const uInt s) {
720 139 : _linewidth = s;
721 139 : _params[LINEWIDTH] = String::toString(_linewidth);
722 139 : }
723 :
724 0 : uInt AnnotationBase::getLineWidth() const {
725 0 : return _linewidth;
726 : }
727 :
728 139 : void AnnotationBase::setSymbolSize(const uInt s) {
729 139 : _symbolsize = s;
730 139 : _params[SYMSIZE] = String::toString(_symbolsize);
731 139 : }
732 :
733 0 : uInt AnnotationBase::getSymbolSize() const {
734 0 : return _symbolsize;
735 : }
736 :
737 139 : void AnnotationBase::setSymbolThickness(const uInt s) {
738 139 : _symbolthickness = s;
739 139 : _params[SYMTHICK] = String::toString(_symbolthickness);
740 139 : }
741 :
742 0 : uInt AnnotationBase::getSymbolThickness() const {
743 0 : return _symbolthickness;
744 : }
745 :
746 139 : void AnnotationBase::setFont(const String& s) {
747 139 : _font = s;
748 139 : _params[FONT] = _font;
749 139 : }
750 :
751 0 : String AnnotationBase::getFont() const {
752 0 : return _font;
753 : }
754 :
755 139 : void AnnotationBase::setFontSize(const uInt s) {
756 139 : _fontsize = s;
757 139 : _params[FONTSIZE] = String::toString(_fontsize);
758 139 : }
759 :
760 0 : uInt AnnotationBase::getFontSize() const {
761 0 : return _fontsize;
762 : }
763 :
764 139 : void AnnotationBase::setFontStyle(const AnnotationBase::FontStyle& fs) {
765 139 : _fontstyle = fs;
766 139 : _params[FONTSTYLE] = fontStyleToString(_fontstyle);
767 139 : }
768 :
769 0 : AnnotationBase::FontStyle AnnotationBase::getFontStyle() const {
770 0 : return _fontstyle;
771 : }
772 :
773 139 : void AnnotationBase::setUseTex(const Bool s) {
774 139 : _usetex = s;
775 139 : _params[USETEX] = _usetex ? "true" : "false";
776 139 : }
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 2 : void AnnotationBase::setLabelPosition(const String& position) {
787 2 : String c = position;
788 2 : c.trim();
789 2 : c.downcase();
790 2 : if (
791 2 : c != "top" && c != "bottom"
792 2 : && c != "left" && c != "right"
793 : ) {
794 : throw AipsError(
795 0 : _class + "::" + __FUNCTION__
796 0 : + ": Unknown label position " + position
797 0 : );
798 : }
799 2 : _labelPos = c;
800 2 : _params[LABELPOS] = _labelPos;
801 2 : }
802 :
803 2 : void AnnotationBase::setLabelOffset(const vector<Int>& offset) {
804 2 : 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 2 : _labelOff = offset;
812 2 : _params[LABELOFF] = "[" + String::toString(offset[0]) + ", " + String::toString(offset[1]) + "]";
813 2 : }
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 139 : void AnnotationBase::setGlobals(
824 : const Vector<Keyword>& globalKeys
825 : ) {
826 2380 : for (
827 278 : Vector<Keyword>::const_iterator iter=globalKeys.begin();
828 2519 : iter != globalKeys.end(); iter++) {
829 2380 : _globals[*iter] = true;
830 : }
831 139 : }
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 705 : String AnnotationBase::lineStyleToString(
867 : const LineStyle style
868 : ) {
869 705 : switch(style) {
870 705 : 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 566 : void AnnotationBase::_checkMixed(
961 : const String& origin, const AnnotationBase::Direction& quantities
962 : ) {
963 566 : Bool isWorld = false;
964 566 : Bool isPixel = false;
965 1132 : Quantity qArg;
966 743 : for (
967 1132 : Direction::const_iterator iter = quantities.begin();
968 1309 : iter != quantities.end(); iter++
969 : ) {
970 2229 : for (uInt i=0; i<2; i++) {
971 2972 : Quantity tQ = i == 0 ? iter->first : iter->second;
972 1486 : Bool pix = tQ.getUnit() == "pix";
973 1486 : Bool world = ! pix;
974 1486 : isWorld = isWorld || world;
975 1486 : isPixel = isPixel || pix;
976 1486 : if (isPixel && isWorld) {
977 : throw AipsError(
978 : origin
979 0 : + ": Mixed world and pixel coordinates not supported"
980 0 : );
981 : }
982 : }
983 : }
984 566 : }
985 :
986 777 : MDirection AnnotationBase::_directionFromQuantities(
987 : const Quantity& q0, const Quantity& q1
988 : ) {
989 1554 : ostringstream oss;
990 777 : oss << q0 << ", " << q1;
991 1554 : Quantity d0 = q0;
992 1554 : Quantity d1 = q1;
993 :
994 1554 : String value = oss.str();
995 777 : if (q0.getUnit() == "pix") {
996 : // both quantities are in pix, this check should
997 : // have been done prior to calling this method
998 278 : Vector<Double> pixel(_csys.nPixelAxes(), 0);
999 139 : pixel[_directionAxes[0]] = q0.getValue();
1000 139 : pixel[_directionAxes[1]] = q1.getValue();
1001 278 : Vector<Double> world;
1002 139 : _csys.toWorld(world, pixel);
1003 139 : Vector<String> axesUnits = _csys.worldAxisUnits();
1004 139 : d0 = Quantity(world[_directionAxes[0]], axesUnits[_directionAxes[0]]);
1005 139 : d1 = Quantity(world[_directionAxes[1]], axesUnits[_directionAxes[1]]);
1006 139 : MDirection::Types csysDirectionType = _csys.directionCoordinate().directionType(false);
1007 139 : 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 139 : _directionRefFrame = csysDirectionType;
1018 : }
1019 : try {
1020 1554 : 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 566 : void AnnotationBase::_checkAndConvertDirections(
1031 : const String& origin, const AnnotationBase::Direction& quantities
1032 : ) {
1033 566 : _checkMixed(origin, quantities);
1034 566 : MDirection::Types csysDirectionRefFrame = _csys.directionCoordinate().directionType(false);
1035 566 : Bool needsConverting = _directionRefFrame != csysDirectionRefFrame;
1036 566 : _convertedDirections.resize(quantities.size());
1037 1309 : for (uInt i=0; i<quantities.size(); i++) {
1038 743 : _convertedDirections[i] = _directionFromQuantities(quantities(i).first, quantities(i).second);
1039 743 : 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 566 : _testConvertToPixel();
1046 566 : }
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 566 : void AnnotationBase::_initColors() {
1062 566 : if (_doneColorInit) {
1063 565 : return;
1064 : }
1065 1 : _colors.insert(make_pair("black", BLACK));
1066 1 : _colors.insert(make_pair("blue", BLUE));
1067 1 : _colors.insert(make_pair("cyan", CYAN));
1068 1 : _colors.insert(make_pair("gray", GRAY));
1069 1 : _colors.insert(make_pair("green", GREEN));
1070 1 : _colors.insert(make_pair("magenta", MAGENTA));
1071 1 : _colors.insert(make_pair("orange", ORANGE));
1072 1 : _colors.insert(make_pair("red", RED));
1073 1 : _colors.insert(make_pair("white", WHITE));
1074 1 : _colors.insert(make_pair("yellow", YELLOW));
1075 :
1076 10 : for (
1077 1 : map<string, RGB>::const_iterator iter=_colors.begin();
1078 21 : iter != _colors.end(); iter++
1079 : ) {
1080 10 : _rgbNameMap[iter->second] = iter->first;
1081 10 : _colorNames.push_back(iter->first);
1082 : }
1083 1 : _doneColorInit = true;
1084 : }
1085 :
1086 0 : std::list<std::string> AnnotationBase::colorChoices() {
1087 0 : _initColors();
1088 0 : return _colorNames;
1089 : }
1090 :
1091 566 : void AnnotationBase::_testConvertToPixel() const {
1092 1132 : Vector<Double> pixel(2);
1093 1132 : Vector<Double> world(2);
1094 1132 : const auto units = _csys.worldAxisUnits();
1095 1132 : const auto end = _convertedDirections.end();
1096 1309 : for (auto iter = _convertedDirections.begin(); iter != end; ++iter) {
1097 743 : world[0] = iter->getAngle().getValue(units[0])[0];
1098 743 : world[1] = iter->getAngle().getValue(units[1])[1];
1099 743 : 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 566 : }
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 4 : String AnnotationBase::_printPixel(const Double& d) {
1164 4 : ostringstream os;
1165 4 : os << std::fixed << std::setprecision(1)
1166 4 : << d << "pix";
1167 8 : return os.str();
1168 : }
1169 :
1170 :
1171 : }
1172 :
1173 :
1174 :
|