Line data Source code
1 : //# TwoSidedShape.cc:
2 : //# Copyright (C) 1999,2000,2001,2002
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: casa-feedback@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 : //# $Id: TwoSidedShape.cc 21292 2012-11-28 14:58:19Z gervandiepen $
27 :
28 : #include <components/ComponentModels/TwoSidedShape.h>
29 : #include <iomanip>
30 : #include <casacore/casa/Arrays/Vector.h>
31 : #include <casacore/casa/Arrays/ArrayLogical.h>
32 : #include <casacore/casa/Containers/Record.h>
33 : #include <casacore/casa/Containers/RecordFieldId.h>
34 : #include <casacore/casa/Containers/RecordInterface.h>
35 : #include <casacore/coordinates/Coordinates/DirectionCoordinate.h>
36 : #include <casacore/casa/Exceptions/Error.h>
37 : #include <casacore/casa/Logging/LogIO.h>
38 : #include <casacore/casa/Logging/LogOrigin.h>
39 : #include <casacore/casa/BasicSL/Constants.h>
40 : #include <casacore/casa/BasicMath/Math.h>
41 : #include <casacore/casa/Quanta/Quantum.h>
42 : #include <casacore/casa/Quanta/QuantumHolder.h>
43 : #include <casacore/casa/Quanta/MVAngle.h>
44 : #include <casacore/casa/Utilities/Assert.h>
45 : #include <casacore/casa/Utilities/Precision.h>
46 : #include <casacore/casa/BasicSL/String.h>
47 :
48 : using namespace casacore;
49 : namespace casa { //# NAMESPACE CASA - BEGIN
50 :
51 0 : TwoSidedShape::~TwoSidedShape() {
52 0 : DebugAssert(ok(), AipsError);
53 0 : }
54 :
55 0 : TwoSidedShape& TwoSidedShape::operator=(const TwoSidedShape& other) {
56 0 : if (this != &other) {
57 0 : ComponentShape::operator=(other);
58 0 : itsMajUnit = other.itsMajUnit;
59 0 : itsMinUnit = other.itsMinUnit;
60 0 : itsPaUnit = other.itsPaUnit;
61 0 : itsMajErr = other.itsMajErr;
62 0 : itsMinErr = other.itsMinErr;
63 0 : itsPaErr = other.itsPaErr;
64 : }
65 0 : DebugAssert(ok(), AipsError);
66 0 : return *this;
67 : }
68 :
69 0 : void TwoSidedShape::setWidth(const Quantity& majorAxis,
70 : const Quantity& minorAxis,
71 : const Quantity& positionAngle) {
72 0 : itsMajUnit = majorAxis.getFullUnit();
73 0 : itsMinUnit = minorAxis.getFullUnit();
74 0 : itsPaUnit = positionAngle.getFullUnit();
75 0 : const Unit rad("rad");
76 0 : setWidthInRad(majorAxis.getValue(rad), minorAxis.getValue(rad),
77 : positionAngle.getValue(rad));
78 0 : DebugAssert(ok(), AipsError);
79 0 : }
80 :
81 0 : void TwoSidedShape::setWidth(const Quantum<Double>& majorAxis,
82 : const Double axialRatio,
83 : const Quantum<Double>& positionAngle) {
84 0 : itsMinUnit = itsMajUnit = majorAxis.getFullUnit();
85 0 : itsPaUnit = positionAngle.getFullUnit();
86 0 : const Unit rad("rad");
87 0 : const Double majWidth = majorAxis.getValue(rad);
88 0 : setWidthInRad(majWidth, majWidth*axialRatio, positionAngle.getValue(rad));
89 0 : DebugAssert(ok(), AipsError);
90 0 : }
91 :
92 0 : Quantum<Double> TwoSidedShape::majorAxis() const {
93 0 : Quantum<Double> retVal(majorAxisInRad(), Unit("rad"));
94 0 : retVal.convert(itsMajUnit);
95 0 : return retVal;
96 0 : }
97 :
98 0 : Quantum<Double> TwoSidedShape::minorAxis() const {
99 0 : Quantum<Double> retVal(minorAxisInRad(), Unit("rad"));
100 0 : retVal.convert(itsMinUnit);
101 0 : return retVal;
102 0 : }
103 :
104 0 : Quantum<Double> TwoSidedShape::positionAngle() const {
105 0 : Quantum<Double> retVal(positionAngleInRad(), Unit("rad"));
106 0 : retVal.convert(itsPaUnit);
107 0 : return retVal;
108 0 : }
109 :
110 0 : Double TwoSidedShape::axialRatio() const {
111 0 : return minorAxisInRad()/majorAxisInRad();
112 : }
113 :
114 0 : void TwoSidedShape::setErrors(const Quantum<Double>& majorAxisError,
115 : const Quantum<Double>& minorAxisError,
116 : const Quantum<Double>& positionAngleError) {
117 0 : if (ComponentShape::badError(majorAxisError) ||
118 0 : ComponentShape::badError(minorAxisError) ||
119 0 : ComponentShape::badError(positionAngleError)) {
120 0 : LogIO logErr(LogOrigin("TwoSidedShape", "setErrors(...)"));
121 : logErr << "The errors must be non-negative angular quantities."
122 0 : << LogIO::EXCEPTION;
123 0 : }
124 0 : itsMajErr = majorAxisError;
125 0 : itsMinErr = minorAxisError;
126 0 : itsPaErr = positionAngleError;
127 0 : }
128 :
129 0 : const Quantum<Double>& TwoSidedShape::majorAxisError() const {
130 0 : return itsMajErr;
131 : }
132 :
133 0 : const Quantum<Double>& TwoSidedShape::minorAxisError() const {
134 0 : return itsMinErr;
135 : }
136 :
137 0 : const Quantum<Double>& TwoSidedShape::positionAngleError() const {
138 0 : return itsPaErr;
139 : }
140 :
141 0 : Double TwoSidedShape::axialRatioError() const {
142 0 : const Unit rad("rad");
143 0 : const Double relErr = itsMajErr.getValue(rad)/majorAxisInRad() +
144 0 : itsMinErr.getValue(rad)/minorAxisInRad();
145 0 : return axialRatio() * relErr;
146 0 : }
147 :
148 0 : void TwoSidedShape::sample(Vector<Double>& scale,
149 : const Vector<MDirection::MVType>& directions,
150 : const MDirection::Ref& refFrame,
151 : const MVAngle& pixelLatSize,
152 : const MVAngle& pixelLongSize) const {
153 0 : ComponentShape::sample(scale, directions, refFrame, pixelLatSize,
154 : pixelLongSize);
155 0 : }
156 :
157 0 : void TwoSidedShape::visibility(Vector<DComplex>& scale,
158 : const Matrix<Double>& uvw,
159 : const Double& frequency) const {
160 0 : ComponentShape::visibility(scale, uvw, frequency);
161 0 : }
162 :
163 0 : void TwoSidedShape::visibility(Matrix<DComplex>& scale,
164 : const Matrix<Double>& uvw,
165 : const Vector<Double>& frequency) const {
166 0 : ComponentShape::visibility(scale, uvw, frequency);
167 0 : }
168 :
169 0 : Bool TwoSidedShape::isSymmetric() const {
170 0 : DebugAssert(ok(), AipsError);
171 0 : return true;
172 : }
173 :
174 0 : uInt TwoSidedShape::nParameters() const {
175 0 : DebugAssert(ok(), AipsError);
176 0 : return 3;
177 : }
178 :
179 0 : void TwoSidedShape::setParameters(const Vector<Double>& newParms) {
180 0 : DebugAssert(newParms.nelements() == nParameters(), AipsError);
181 0 : DebugAssert(newParms(0) >= newParms(1), AipsError);
182 0 : DebugAssert(abs(newParms(2)) <= C::_2pi, AipsError);
183 0 : setWidthInRad(newParms(0), newParms(1), newParms(2));
184 0 : DebugAssert(ok(), AipsError);
185 0 : }
186 :
187 0 : Vector<Double> TwoSidedShape::parameters() const {
188 0 : DebugAssert(ok(), AipsError);
189 0 : Vector<Double> compParms(3);
190 0 : compParms(0) = majorAxisInRad();
191 0 : compParms(1) = minorAxisInRad();
192 0 : compParms(2) = positionAngleInRad();
193 0 : return compParms;
194 0 : }
195 :
196 0 : void TwoSidedShape::setErrors(const Vector<Double>& newErrors) {
197 0 : DebugAssert(newErrors.nelements() == nParameters(), AipsError);
198 0 : DebugAssert(allGE(newErrors, 0.0), AipsError);
199 0 : const Unit rad("rad");
200 0 : itsMajErr.setValue(newErrors(0));
201 0 : itsMajErr.setUnit(rad);
202 0 : itsMinErr.setValue(newErrors(1));
203 0 : itsMinErr.setUnit(rad);
204 0 : itsPaErr.setValue(newErrors(2));
205 0 : itsPaErr.setUnit(rad);
206 0 : DebugAssert(ok(), AipsError);
207 0 : }
208 :
209 0 : Vector<Double> TwoSidedShape::errors() const {
210 0 : DebugAssert(ok(), AipsError);
211 0 : Vector<Double> compErrors(3);
212 0 : compErrors(0) = itsMajErr.getBaseValue();
213 0 : compErrors(1) = itsMinErr.getBaseValue();
214 0 : compErrors(2) = itsPaErr.getBaseValue();
215 0 : return compErrors;
216 0 : }
217 :
218 0 : Vector<Double> TwoSidedShape::optParameters() const {
219 0 : DebugAssert(ok(), AipsError);
220 0 : return Vector<Double>(0);
221 : }
222 :
223 0 : void TwoSidedShape::setOptParameters(const Vector<Double>& newOptParms){
224 0 : DebugAssert(ok(), AipsError);
225 : // squash compiler warning, maybe just get rid of DebugAssert statement
226 0 : if (newOptParms.empty()) {};
227 0 : }
228 :
229 0 : Bool TwoSidedShape::fromRecord(String& errorMessage,
230 : const RecordInterface& record) {
231 0 : if (!ComponentShape::fromRecord(errorMessage, record)) return false;
232 0 : Quantum<Double> majorAxis, minorAxis, pa;
233 0 : if (!fromAngQRecord(majorAxis, errorMessage, "majoraxis", record) ||
234 0 : !fromAngQRecord(minorAxis, errorMessage, "minoraxis", record) ||
235 0 : !fromAngQRecord(pa, errorMessage, "positionangle", record)) {
236 0 : errorMessage += "Shape not changed\n";
237 0 : return false;
238 : }
239 0 : const Unit rad("rad");
240 0 : const Double majorAxisInRad = majorAxis.getValue(rad);
241 0 : const Double minorAxisInRad = minorAxis.getValue(rad);
242 : // // The near function is necessary for Intel processors (and doesn't hurt for
243 : // // other architectures) because of the extra precision that floating point
244 : // // variables have when returned in floating point registers. See
245 : // // http://aips2.nrao.edu/mail/aips2-lib/1101 for a discussion of this. The
246 : // // near function was added here and in the setMinorAxis function to fix
247 : // // defect AOCso00071
248 0 : if (majorAxisInRad < minorAxisInRad &&
249 0 : !near(minorAxisInRad, minorAxisInRad, 2*C::dbl_epsilon)) {
250 0 : errorMessage += "The major axis cannot be smaller than the minor axis\n";
251 0 : return false;
252 : }
253 0 : setWidth(majorAxis, minorAxis, pa);
254 0 : if (!fromAngQRecord(majorAxis, errorMessage, "majoraxiserror", record) ||
255 0 : !fromAngQRecord(minorAxis, errorMessage, "minoraxiserror", record) ||
256 0 : !fromAngQRecord(pa, errorMessage, "positionangleerror", record)) {
257 0 : errorMessage += "Shape errors not changed\n";
258 0 : return false;
259 : }
260 0 : setErrors(majorAxis, minorAxis, pa);
261 0 : DebugAssert(ok(), AipsError);
262 0 : return true;
263 0 : }
264 :
265 0 : Bool TwoSidedShape::toRecord(String& errorMessage,
266 : RecordInterface& record) const {
267 0 : DebugAssert(ok(), AipsError);
268 0 : if (!ComponentShape::toRecord(errorMessage, record)) return false;
269 : {
270 0 : const QuantumHolder qHolder(majorAxis());
271 0 : Record qRecord;
272 0 : if (!qHolder.toRecord(errorMessage, qRecord)) {
273 0 : errorMessage += "Cannot convert the major axis to a record\n";
274 0 : return false;
275 : }
276 0 : record.defineRecord(RecordFieldId("majoraxis"), qRecord);
277 0 : }
278 : {
279 0 : const QuantumHolder qHolder(minorAxis());
280 0 : Record qRecord;
281 0 : if (!qHolder.toRecord(errorMessage, qRecord)) {
282 0 : errorMessage += "Cannot convert the minor axis to a record\n";
283 0 : return false;
284 : }
285 0 : record.defineRecord(RecordFieldId("minoraxis"), qRecord);
286 0 : }
287 : {
288 0 : const QuantumHolder qHolder(positionAngle());
289 0 : Record qRecord;
290 0 : if (!qHolder.toRecord(errorMessage, qRecord)) {
291 0 : errorMessage += "Cannot convert the position angle to a record\n";
292 0 : return false;
293 : }
294 0 : record.defineRecord(RecordFieldId("positionangle"), qRecord);
295 0 : }
296 : {
297 0 : const QuantumHolder qHolder(majorAxisError());
298 0 : Record qRecord;
299 0 : if (!qHolder.toRecord(errorMessage, qRecord)) {
300 0 : errorMessage += "Cannot convert the major axis error to a record\n";
301 0 : return false;
302 : }
303 0 : record.defineRecord(RecordFieldId("majoraxiserror"), qRecord);
304 0 : }
305 : {
306 0 : const QuantumHolder qHolder(minorAxisError());
307 0 : Record qRecord;
308 0 : if (!qHolder.toRecord(errorMessage, qRecord)) {
309 0 : errorMessage += "Cannot convert the minor axis error to a record\n";
310 0 : return false;
311 : }
312 0 : record.defineRecord(RecordFieldId("minoraxiserror"), qRecord);
313 0 : }
314 : {
315 0 : const QuantumHolder qHolder(positionAngleError());
316 0 : Record qRecord;
317 0 : if (!qHolder.toRecord(errorMessage, qRecord)) {
318 0 : errorMessage += "Cannot convert the position angle error to a record\n";
319 0 : return false;
320 : }
321 0 : record.defineRecord(RecordFieldId("positionangleerror"), qRecord);
322 0 : }
323 0 : return true;
324 : }
325 :
326 0 : Bool TwoSidedShape::convertUnit(String& errorMessage,
327 : const RecordInterface& record) {
328 0 : const Unit deg("deg");
329 : {
330 0 : const String fieldString("majoraxis");
331 0 : if (!record.isDefined(fieldString)) {
332 0 : errorMessage += "The 'majoraxis' field does not exist\n";
333 0 : return false;
334 : }
335 0 : const RecordFieldId field(fieldString);
336 0 : if (!((record.dataType(field) == TpString) &&
337 0 : (record.shape(field) == IPosition(1,1)))) {
338 0 : errorMessage += "The 'majoraxis' field must be a string\n";
339 0 : errorMessage += "(but not a vector of strings)\n";
340 0 : return false;
341 : }
342 0 : const Unit unit = Unit(record.asString(field));
343 0 : if (unit != deg) {
344 : errorMessage +=
345 0 : "Cannot convert the major axis width to a non angular unit";
346 0 : return false;
347 : }
348 0 : itsMajUnit = unit;
349 0 : }
350 : {
351 0 : const String fieldString("minoraxis");
352 0 : if (!record.isDefined(fieldString)) {
353 0 : errorMessage += "The 'minoraxis' field does not exist\n";
354 0 : return false;
355 : }
356 0 : const RecordFieldId field(fieldString);
357 0 : if (!((record.dataType(field) == TpString) &&
358 0 : (record.shape(field) == IPosition(1,1)))) {
359 0 : errorMessage += "The 'minoraxis' field must be a string\n";
360 0 : errorMessage += "(but not a vector of strings)\n";
361 0 : return false;
362 : }
363 0 : const Unit unit = Unit(record.asString(field));
364 0 : if (unit != deg) {
365 : errorMessage +=
366 0 : "Cannot convert the minor axis width to a non angular unit";
367 0 : return false;
368 : }
369 0 : itsMinUnit = unit;
370 0 : }
371 : {
372 0 : const String fieldString("positionangle");
373 0 : if (!record.isDefined(fieldString)) {
374 0 : errorMessage += "The 'positionangle' field does not exist\n";
375 0 : return false;
376 : }
377 0 : const RecordFieldId field(fieldString);
378 0 : if (!((record.dataType(field) == TpString) &&
379 0 : (record.shape(field) == IPosition(1,1)))) {
380 0 : errorMessage += "The 'positionangle' field must be a string\n";
381 0 : errorMessage += "(but not a vector of strings)\n";
382 0 : return false;
383 : }
384 0 : const Unit unit = Unit(record.asString(field));
385 0 : if (unit != deg) {
386 : errorMessage +=
387 0 : "Cannot convert the position angle to a non angular unit";
388 0 : return false;
389 : }
390 0 : itsPaUnit = unit;
391 0 : }
392 0 : DebugAssert(ok(), AipsError);
393 0 : return true;
394 0 : }
395 :
396 0 : Bool TwoSidedShape::ok() const {
397 : // The LogIO class is only constructed if an error is detected for
398 : // performance reasons. Both function static and file static variables
399 : // where considered and rejected for this purpose.
400 0 : if (!ComponentShape::ok()) return false;
401 0 : const Unit deg("deg");
402 0 : if (itsMajUnit != deg) {
403 0 : LogIO logErr(LogOrigin("TwoSidedCompRep", "ok()"));
404 : logErr << LogIO::SEVERE << "The major axis does not have angular units."
405 0 : << LogIO::POST;
406 0 : return false;
407 0 : }
408 0 : if (itsMinUnit != deg) {
409 0 : LogIO logErr(LogOrigin("TwoSidedCompRep", "ok()"));
410 : logErr << LogIO::SEVERE << "The minor axis does not have angular units."
411 0 : << LogIO::POST;
412 0 : return false;
413 0 : }
414 0 : if (itsPaUnit != deg) {
415 0 : LogIO logErr(LogOrigin("TwoSidedCompRep", "ok()"));
416 : logErr << LogIO::SEVERE <<"The position angle does not have angular units."
417 0 : << LogIO::POST;
418 0 : return false;
419 0 : }
420 0 : return true;
421 0 : }
422 :
423 0 : TwoSidedShape::TwoSidedShape()
424 : :ComponentShape(),
425 0 : itsMajUnit("arcmin"),
426 0 : itsMinUnit("arcmin"),
427 0 : itsPaUnit("deg"),
428 0 : itsMajErr(0, "arcmin"),
429 0 : itsMinErr(0, "arcmin"),
430 0 : itsPaErr(0, "deg")
431 : {
432 0 : DebugAssert(ok(), AipsError);
433 0 : }
434 :
435 0 : TwoSidedShape::TwoSidedShape(const MDirection& direction,
436 : const Unit& majorAxisUnit,
437 : const Unit& minorAxisUnit,
438 0 : const Unit& paUnit)
439 : :ComponentShape(direction),
440 0 : itsMajUnit(majorAxisUnit),
441 0 : itsMinUnit(minorAxisUnit),
442 0 : itsPaUnit(paUnit),
443 0 : itsMajErr(0, "arcmin"),
444 0 : itsMinErr(0, "arcmin"),
445 0 : itsPaErr(0, "deg")
446 : {
447 0 : }
448 :
449 0 : TwoSidedShape::TwoSidedShape(const TwoSidedShape& other)
450 : :ComponentShape(other),
451 0 : itsMajUnit(other.itsMajUnit),
452 0 : itsMinUnit(other.itsMinUnit),
453 0 : itsPaUnit(other.itsPaUnit),
454 0 : itsMajErr(other.itsMajErr),
455 0 : itsMinErr(other.itsMinErr),
456 0 : itsPaErr(other.itsPaErr)
457 : {
458 0 : DebugAssert(ok(), AipsError);
459 0 : }
460 :
461 :
462 0 : Vector<Double> TwoSidedShape::toPixel (const DirectionCoordinate& dirCoord) const
463 : //
464 : // pars(0) = long cen abs pix
465 : // pars(1) = lat cen abs pix
466 : // pars(2) = major pix
467 : // pars(3) = minor pix
468 : // pars(4) = pa radians; pos +x (long) -> +y (lat)
469 : //
470 : {
471 0 : LogIO os(LogOrigin("TwoSidedShape", "toPixel"));
472 0 : Vector<Double> parameters(5);
473 :
474 : // Do locations
475 :
476 0 : Vector<Double> pixelCen = ComponentShape::toPixel (dirCoord);
477 0 : parameters(0) = pixelCen(0);
478 0 : parameters(1) = pixelCen(1);
479 :
480 : // Now convert the tip of the major axis to x/y pixel coordinates
481 :
482 0 : const MDirection dirRef = refDirection();
483 0 : Quantum<Double> majorWorld = majorAxis();
484 0 : Quantum<Double> paMajor = positionAngle();
485 0 : majorWorld.scale(0.5);
486 0 : Vector<Double> majorCart = widthToCartesian (majorWorld, paMajor, dirRef, dirCoord, pixelCen);
487 :
488 : // Position angle of major axis.
489 : // atan2 gives pos +x (long) -> +y (lat). put in range +/- pi
490 :
491 0 : MVAngle pa(atan2(majorCart(1), majorCart(0)));
492 0 : pa();
493 :
494 : // I cannot just add 90deg to the world position angle. It is 90deg in the
495 : // pixel coordinate frame, not the world frame. So I have to work
496 : // my way along the minor axis in pixel coordinates and locate
497 : // the tip of the minor axis iteratively. The algorithm
498 : // below could be much smarter/faster with a binary search.
499 :
500 0 : Quantum<Double> minorWorld = minorAxis();
501 0 : Quantum<Double> paMinor = paMajor + Quantum<Double>(C::pi/2.0, Unit("rad"));
502 0 : minorWorld.scale(0.5);
503 : //
504 0 : Double dX = sin(pa.radian());
505 0 : Double dY = cos(pa.radian());
506 : //
507 0 : Vector<Double> posPix = pixelCen.copy();
508 0 : MDirection posWorld;
509 0 : MVDirection mvdRef = dirRef.getValue();
510 0 : Vector<Double> prevPosPix(2);
511 : //
512 0 : Double minorWorldRad = minorWorld.getValue(Unit("rad"));
513 0 : Double sep = 0.0;
514 0 : Double prevSep = 0.0;
515 0 : Bool more = true;
516 0 : while (more) {
517 0 : dirCoord.toWorld(posWorld, posPix);
518 0 : MVDirection mvd = posWorld.getValue();
519 0 : sep = mvdRef.separation(mvd);
520 0 : if (sep > minorWorldRad) break;
521 : //
522 0 : prevPosPix = posPix;
523 0 : prevSep = sep;
524 : //
525 0 : posPix(0) += dX;
526 0 : posPix(1) += dY;
527 0 : }
528 0 : Double frac = (minorWorldRad - prevSep) / (sep - prevSep);
529 0 : Double fracX = dX * frac;
530 0 : Double fracY = dY * frac;
531 : //
532 0 : Vector<Double> minorCart(2);
533 0 : minorCart(0) = prevPosPix(0) + fracX - pixelCen(0);
534 0 : minorCart(1) = prevPosPix(1) + fracY - pixelCen(1);
535 : //
536 0 : Double tmp1 = 2.0 * hypot(majorCart(0), majorCart(1));
537 0 : Double tmp2 = 2.0 * hypot(minorCart(0), minorCart(1));
538 : //
539 0 : parameters(2) = max(tmp1,tmp2);
540 0 : parameters(3) = min(tmp1,tmp2);
541 0 : parameters(4) = pa.radian();
542 : //
543 0 : return parameters;
544 0 : }
545 :
546 0 : Bool TwoSidedShape::fromPixel (const Vector<Double>& parameters,
547 : const DirectionCoordinate& dirCoord)
548 : //
549 : // pars(0) = long cen abs pix
550 : // pars(1) = lat cen abs pix
551 : // pars(2) = major pix
552 : // pars(3) = minor pix
553 : // pars(4) = pa radians; pos +x (long) -> +y (lat)
554 : //
555 : {
556 : // Direction first
557 :
558 0 : Vector<Double> pixelCen(2);
559 0 : pixelCen(0) = parameters(0);
560 0 : pixelCen(1) = parameters(1);
561 0 : ComponentShape::fromPixel (pixelCen, dirCoord);
562 : // Shape. First put x/y p.a. into +y -> -x system
563 :
564 0 : Double pa0 = parameters(4) - C::pi_2;
565 0 : MDirection tipMajor = directionFromCartesian (parameters(2), pa0, dirCoord, pixelCen);
566 : //
567 0 : pa0 += C::pi_2; // minor axis position angle
568 0 : MDirection tipMinor = directionFromCartesian (parameters(3), pa0, dirCoord, pixelCen);
569 :
570 : // Find tip directions
571 0 : const MDirection& directionRef = refDirection();
572 0 : MVDirection mvdRef = directionRef.getValue();
573 0 : MVDirection mvdMajor = tipMajor.getValue();
574 0 : MVDirection mvdMinor = tipMinor.getValue();
575 :
576 : // Separations
577 :
578 0 : Double tmp1 = 2 * mvdRef.separation(mvdMajor) * 3600 * 180.0 / C::pi;
579 0 : Double tmp2 = 2 * mvdRef.separation(mvdMinor) * 3600 * 180.0 / C::pi;
580 :
581 0 : Quantity majorAxis(max(tmp1,tmp2), Unit("arcsec"));
582 0 : Quantity minorAxis(min(tmp1,tmp2), Unit("arcsec"));
583 0 : Bool flipped = tmp2 > tmp1;
584 0 : Quantity pa;
585 0 : if (!flipped) {
586 0 : pa = mvdRef.positionAngle(mvdMajor, Unit("deg"));
587 : } else {
588 0 : pa = mvdRef.positionAngle(mvdMinor, Unit("deg"));
589 : }
590 0 : setWidth (majorAxis, minorAxis, pa);
591 0 : return flipped;
592 0 : }
593 :
594 0 : Vector<Double> TwoSidedShape::widthToCartesian (const Quantum<Double>& width,
595 : const Quantum<Double>& pa,
596 : const MDirection& dirRef,
597 : const DirectionCoordinate& dirCoord,
598 : const Vector<Double>& pixelCen) const
599 : {
600 :
601 : // Find MDirection of tip of axis
602 :
603 0 : MDirection dirTip = dirRef;
604 0 : dirTip.shiftAngle(width, pa);
605 :
606 : // Convert to pixel
607 :
608 0 : Vector<Double> pixelTip(2);
609 0 : if (!dirCoord.toPixel(pixelTip, dirTip)) {
610 0 : LogIO os(LogOrigin("TwoSidedShape", "widthToCartesian"));
611 : os << "DirectionCoordinate conversion to pixel failed because "
612 0 : << dirCoord.errorMessage() << LogIO::EXCEPTION;
613 0 : }
614 :
615 : // Find offset cartesian components
616 :
617 0 : Vector<Double> cart(2);
618 0 : cart(0) = pixelTip(0) - pixelCen(0);
619 0 : cart(1) = pixelTip(1) - pixelCen(1);
620 0 : return cart;
621 0 : }
622 :
623 0 : MDirection TwoSidedShape::directionFromCartesian (Double width, Double pa,
624 : const DirectionCoordinate& dirCoord,
625 : const Vector<Double>& pixelCen) const
626 : {
627 :
628 : // Now find tips of major and minor axes in pixel coordinates
629 : // and convert to world
630 :
631 0 : Double z = width / 2.0;
632 0 : Double x = -z * sin(pa);
633 0 : Double y = z * cos(pa);
634 0 : MDirection dir;
635 0 : Vector<Double> pixelTip(2);
636 0 : pixelTip(0) = pixelCen(0) + x;
637 0 : pixelTip(1) = pixelCen(1) + y;
638 0 : ThrowIf(
639 : ! dirCoord.toWorld(dir, pixelTip),
640 : "DirectionCoordinate conversion failed because "
641 : + dirCoord.errorMessage()
642 : );
643 0 : return dir;
644 0 : }
645 :
646 0 : String TwoSidedShape::sizeToString(
647 : Quantity major, Quantity minor, Quantity posangle,
648 : Bool includeUncertainties, Quantity majorErr,
649 : Quantity minorErr, Quantity posanErr
650 : ) {
651 : // Inputs all as angle quanta
652 0 : Vector<String> angUnits(5);
653 0 : angUnits[0] = "deg";
654 0 : angUnits[1] = "arcmin";
655 0 : angUnits[2] = "arcsec";
656 0 : angUnits[3] = "marcsec";
657 0 : angUnits[4] = "uarcsec";
658 : // First force position angle to be between 0 and 180 deg
659 0 : if(posangle.getValue() < 0) {
660 0 : posangle += Quantity(180, "deg");
661 : }
662 :
663 0 : String prefUnits;
664 0 : Quantity vmax(max(fabs(major.getValue("arcsec")), fabs(minor.getValue("arcsec"))), "arcsec");
665 :
666 0 : for (uInt i=0; i<angUnits.size(); i++) {
667 0 : prefUnits = angUnits[i];
668 0 : if(vmax.getValue(prefUnits) > 1) {
669 0 : break;
670 : }
671 : }
672 0 : major.convert(prefUnits);
673 0 : minor.convert(prefUnits);
674 0 : majorErr.convert(prefUnits);
675 0 : minorErr.convert(prefUnits);
676 :
677 0 : Double vmaj = major.getValue();
678 0 : Double vmin = minor.getValue();
679 :
680 : // Formatting as "value +/- err" for symmetric errors
681 :
682 0 : Double dmaj = majorErr.getValue();
683 0 : Double dmin = minorErr.getValue();
684 : // position angle is always in degrees cuz users like that
685 0 : Double pa = posangle.getValue("deg");
686 0 : Double dpa = posanErr.getValue("deg");
687 :
688 0 : Vector<Double> majVec(2), minVec(2), paVec(2);
689 0 : majVec[0] = vmaj;
690 0 : majVec[1] = dmaj;
691 0 : minVec[0] = vmin;
692 0 : minVec[1] = dmin;
693 0 : paVec[0] = pa;
694 0 : paVec[1] = dpa;
695 0 : uInt precision1 = precisionForValueErrorPairs(majVec, minVec);
696 0 : uInt precision2 = precisionForValueErrorPairs(paVec, Vector<Double>(0));
697 :
698 0 : ostringstream summary;
699 0 : summary << std::fixed << std::setprecision(precision1);
700 0 : summary << " --- major axis FWHM: " << major.getValue();
701 0 : if (includeUncertainties) {
702 0 : if (majorErr.getValue() == 0) {
703 0 : summary << " " << prefUnits << " (fixed)" << endl;
704 : }
705 : else {
706 0 : summary << " +/- " << majorErr.getValue()
707 0 : << " " << prefUnits << endl;
708 : }
709 : }
710 : else {
711 0 : summary << " " << prefUnits << endl;
712 : }
713 0 : summary << " --- minor axis FWHM: " << minor.getValue();
714 0 : if (includeUncertainties) {
715 0 : if (minorErr.getValue() == 0) {
716 0 : summary << " " << prefUnits << " (fixed)" << endl;
717 : }
718 : else {
719 0 : summary << " +/- " << minorErr.getValue()
720 0 : << " " << prefUnits << endl;
721 : }
722 : }
723 : else {
724 0 : summary << " " << prefUnits << endl;
725 : }
726 0 : summary << std::setprecision(precision2);
727 0 : summary << " --- position angle: " << pa;
728 0 : if (includeUncertainties) {
729 0 : if (dpa == 0) {
730 0 : summary << "deg (fixed)" << endl;
731 : }
732 : else {
733 0 : summary << " +/- " << dpa << " deg" << endl;
734 : }
735 : }
736 : else {
737 0 : summary << " deg" << endl;
738 : }
739 0 : return summary.str();
740 0 : }
741 :
742 :
743 : // Local Variables:
744 : // compile-command: "gmake OPTLIB=1 TwoSidedShape"
745 : // End:
746 :
747 :
748 : } //# NAMESPACE CASA - END
749 :
|