source: trunk/MagicSoft/Mars/mastro/MAstroCatalog.cc@ 7884

Last change on this file since 7884 was 7491, checked in by tbretz, 19 years ago
*** empty log message ***
File size: 40.5 KB
Line 
1/* ======================================================================== *\
2!
3! *
4! * This file is part of MARS, the MAGIC Analysis and Reconstruction
5! * Software. It is distributed to you in the hope that it can be a useful
6! * and timesaving tool in analysing Data of imaging Cerenkov telescopes.
7! * It is distributed WITHOUT ANY WARRANTY.
8! *
9! * Permission to use, copy, modify and distribute this software and its
10! * documentation for any purpose is hereby granted without fee,
11! * provided that the above copyright notice appear in all copies and
12! * that both that copyright notice and this permission notice appear
13! * in supporting documentation. It is provided "as is" without expressed
14! * or implied warranty.
15! *
16!
17!
18! Author(s): Thomas Bretz, 03/2004 <mailto:tbretz@astro.uni-wuerzburg.de>
19!
20! Copyright: MAGIC Software Development, 2002-2005
21!
22!
23\* ======================================================================== */
24
25//////////////////////////////////////////////////////////////////////////////
26//
27// MAstroCatalog
28// =============
29//
30// THIS IMPLEMENTATION IS PRELIMINARY AND WILL BE MERGED WITH
31// SOME PARTS OF THE DRIVE SOFTWARE SOON!
32//
33//
34// Catalogs:
35// ---------
36//
37// To be able to use this class you need a catalog file suppored by
38// MAstroCatalog.
39// Catalog files can be found at
40// http://magic.astro.uni-wuerzburg.de/mars/catalogs.html
41// You must copy the file into the directory from which you start your macro
42// or give an abolute path loading the catalog.
43//
44//
45// Usage:
46// ------
47//
48// To display a starfield you must have a supported catalog, then do:
49//
50// MTime time;
51// // Time for which to get the picture
52// time.Set(2004, 2, 28, 20, 14, 7);
53// // Current observatory
54// MObservatory magic1;
55// // Right Ascension [h] and declination [deg] of source
56// // Currently 'perfect' pointing is assumed
57// const Double_t ra = MAstro::Hms2Rad(5, 34, 31.9);
58// const Double_t dec = MAstro::Dms2Rad(22, 0, 52.0);
59// MAstroCatalog stars;
60// // Magnitude up to which the stars are loaded from the catalog
61// stars.SetLimMag(6);
62// // Radius of FOV around the source position to load the stars
63// stars.SetRadiusFOV(3);
64// // Source position
65// stars.SetRaDec(ra, dec);
66// // Catalog to load (here: Bright Star Catalog V5)
67// stars.ReadBSC("bsc5.dat");
68// // Obersavatory and time to also get local coordinate information
69// stars.SetObservatory(magic1);
70// stars.SetTime(time);
71// // Enable interactive GUI
72// stars.SetGuiActive();
73// //Clone the catalog due to the validity range of the instance
74// TObject *o = stars.Clone();
75// o->SetBit(kCanDelete);
76// o->Draw();
77//
78// If no time and/or Obervatory location is given no local coordinate
79// information is displayed.
80//
81//
82// Coordinate Transformation:
83// -------------------------
84// The conversion from sky coordinates to local coordinates is done using
85// MAstroSky2Local which does a simple rotation of the coordinate system.
86// This is inaccurate in the order of 30arcsec due to ignorance of all
87// astrometrical corrections (nutation, precission, abberation, ...)
88//
89//
90// GUI:
91// ----
92// * If the gui is interactive you can use the cursor keys to change
93// the position you are looking at and with plus/minus you
94// can (un)zoom the FOV (Field Of View)
95// * The displayed values mean the following:
96// + alpha: Right Ascension
97// + delta: Declination
98// + theta: zenith distance / zenith angle
99// + phi: azimuth angle
100// + rho: angle of rotation sky-coordinate system vs local-
101// coordinate system
102// + time of display
103// * Move the mouse on top of the grid points or the stars to get
104// more setailed information.
105// * Enable the event-info in a canvas to see the current
106// ObjectInfo=tooltip-text
107// * call SetNoToolTips to supress the tooltips
108// * the blue lines are the local coordinat system
109// * the red lines are sky coordinate system
110//
111//
112// ToDo:
113// -----
114// - replace MVetcor3 by a more convinient class. Maybe use TExMap, too.
115// - change tooltips to multi-line tools tips as soon as root
116// supports them
117// - a derived class is missing which supports all astrometrical
118// correction (base on slalib and useable in Cosy)
119// - Implement a general loader for heasarc catlogs, see
120// http://heasarc.gsfc.nasa.gov/W3Browse/star-catalog/
121//
122// Class Version 2:
123// + MAttLine fAttLineSky; // Line Style and color for sky coordinates
124// + MAttLine fAttLineLocal; // Line Style and color for local coordinates
125// + added new base class TAttMarker
126//
127//////////////////////////////////////////////////////////////////////////////
128#include "MAstroCatalog.h"
129
130#include <errno.h> // strerror
131#include <fstream> // ifstream
132#include <stdlib.h> // ati, atof
133#include <limits.h> // INT_MAX (Suse 7.3/gcc 2.95)
134
135#include <KeySymbols.h> // kKey_*
136
137#include <TLine.h> // TLine
138#include <TMarker.h> // TMarker
139#include <TCanvas.h> // TCanvas
140#include <TArrayI.h> // TArrayI
141#include <TGToolTip.h> // TGToolTip
142#include <TPaveText.h> // TPaveText
143
144#include "MLog.h"
145#include "MLogManip.h"
146
147#include "MTime.h"
148#include "MString.h"
149#include "MAstro.h"
150#include "MAstroSky2Local.h"
151#include "MObservatory.h"
152
153#undef DEBUG
154//#define DEBUG
155
156#ifdef DEBUG
157#include <TStopwatch.h>
158#endif
159
160ClassImp(MAttLine);
161ClassImp(MAstroCatalog);
162
163using namespace std;
164
165// --------------------------------------------------------------------------
166//
167// Default Constructor. Set Default values:
168// fLimMag = 99
169// fRadiusFOV = 90
170//
171MAstroCatalog::MAstroCatalog() : fLimMag(99), fRadiusFOV(90), fToolTip(0), fObservatory(0), fTime(0)
172{
173 fList.SetOwner();
174 fMapG.SetOwner();
175
176 fToolTip = gROOT->IsBatch() || !gClient ? 0 : new TGToolTip(0, "", 0);
177
178 fAttLineSky.SetLineStyle(kDashDotted);
179 fAttLineLocal.SetLineStyle(kDashDotted);
180
181 fAttLineSky.SetLineColor(kRed);
182 fAttLineLocal.SetLineColor(kBlue);
183
184 SetMarkerColor(kBlack);
185 SetMarkerStyle(kCircle);
186}
187
188// --------------------------------------------------------------------------
189//
190// Destructor. Delete fTime, fObservatory. Disconnect signal. delete tooltip.
191// Delete Map with gui primitives
192//
193MAstroCatalog::~MAstroCatalog()
194{
195 // First disconnect the EventInfo...
196 // FIXME: There must be an easier way!
197 TIter Next(gROOT->GetListOfCanvases());
198 TCanvas *c;
199 while ((c=(TCanvas*)Next()))
200 c->Disconnect("ProcessedEvent(Int_t,Int_t,Int_t,TObject*)", this,
201 "EventInfo(Int_t,Int_t,Int_t,TObject*)");
202
203 // Now delete the data members
204 if (fTime)
205 delete fTime;
206 if (fObservatory)
207 delete fObservatory;
208
209 if (fToolTip)
210 {
211 fToolTip->Hide();
212 delete fToolTip;
213 }
214}
215
216// --------------------------------------------------------------------------
217//
218// Snippet to for reading catalog files.
219//
220TString MAstroCatalog::FindToken(TString &line, Char_t tok)
221{
222 Ssiz_t token = line.First(tok);
223 if (token<0)
224 {
225 const TString copy(line);
226 line = "";
227 return copy;
228 }
229
230 const TString res = line(0, token);
231 line.Remove(0, token+1);
232 return res;
233}
234
235// --------------------------------------------------------------------------
236//
237// return int correspoding to TString
238//
239Int_t MAstroCatalog::atoi(const TString &s)
240{
241 return const_cast<TString&>(s).Atoi();
242}
243
244// --------------------------------------------------------------------------
245//
246// return float correspoding to TString
247//
248Float_t MAstroCatalog::atof(const TString &s)
249{
250 return const_cast<TString&>(s).Atof();
251}
252
253// --------------------------------------------------------------------------
254//
255// Read from a xephem catalog, set bit kHasChahanged.
256// Already read data is not deleted. To delete the stored data call
257// Delete().
258//
259Int_t MAstroCatalog::ReadXephem(TString catalog)
260{
261 gLog << inf << "Reading Xephem catalog: " << catalog << endl;
262
263 ifstream fin(catalog);
264 if (!fin)
265 {
266 gLog << err << "Cannot open catalog file " << catalog << ": ";
267 gLog << strerror(errno) << endl;
268 return 0;
269 }
270
271 Int_t add =0;
272 Int_t cnt =0;
273 Int_t line=0;
274
275 Double_t maxmag=0;
276
277 while (1)
278 {
279 TString row;
280 row.ReadLine(fin);
281 if (!fin)
282 break;
283
284 line++;
285
286 if (row[0]=='#')
287 continue;
288
289 TString line(row);
290
291 TString name = FindToken(line);
292 TString dummy = FindToken(line);
293 TString r = FindToken(line);
294 TString d = FindToken(line);
295 TString m = FindToken(line);
296 TString epoch = FindToken(line);
297
298 if (name.IsNull() || r.IsNull() || d.IsNull() || m.IsNull() || epoch.IsNull())
299 {
300 gLog << warn << "Invalid Entry Line #" << line << ": " << row << endl;
301 continue;
302 }
303
304 cnt++;
305
306 const Double_t mag = atof(m);
307
308 maxmag = TMath::Max(maxmag, mag);
309
310 if (mag>fLimMag)
311 continue;
312
313 if (epoch!="2000")
314 {
315 gLog << warn << "Epoch != 2000... skipped." << endl;
316 continue;
317 }
318
319 Double_t ra0, dec0;
320 MAstro::Coordinate2Angle(r, ra0);
321 MAstro::Coordinate2Angle(d, dec0);
322
323 ra0 *= TMath::Pi()/12;
324 dec0 *= TMath::Pi()/180;
325
326 if (AddObject(ra0, dec0, mag, name))
327 add++;
328 }
329 gLog << inf << "Read " << add << " out of " << cnt << " (Total max mag=" << maxmag << ")" << endl;
330
331 return add;
332}
333
334// --------------------------------------------------------------------------
335//
336// Read from a NGC2000 catalog. set bit kHasChanged
337// Already read data is not deleted. To delete the stored data call
338// Delete().
339//
340Int_t MAstroCatalog::ReadNGC2000(TString catalog)
341{
342 gLog << inf << "Reading NGC2000 catalog: " << catalog << endl;
343
344 ifstream fin(catalog);
345 if (!fin)
346 {
347 gLog << err << "Cannot open catalog file " << catalog << ": ";
348 gLog << strerror(errno) << endl;
349 return 0;
350 }
351
352 Int_t add=0;
353 Int_t cnt=0;
354 Int_t n =0;
355
356 Double_t maxmag=0;
357
358 while (1)
359 {
360 TString row;
361 row.ReadLine(fin);
362 if (!fin)
363 break;
364
365 cnt++;
366
367 const Int_t rah = atoi(row(13, 2));
368 const Float_t ram = atof(row(16, 4));
369 const Char_t decs = row(22);
370 const Int_t decd = atoi(row(23, 2));
371 const Int_t decm = atoi(row(26, 2));
372 const TString m = row(43, 4);
373
374 if (m.Strip().IsNull())
375 continue;
376
377 n++;
378
379 const Double_t mag = atof(m);
380
381 maxmag = TMath::Max(maxmag, mag);
382
383 if (mag>fLimMag)
384 continue;
385
386 const Double_t ra = MAstro::Hms2Rad(rah, (int)ram, fmod(ram, 1)*60);
387 const Double_t dec = MAstro::Dms2Rad(decd, decm, 0, decs);
388
389 if (AddObject(ra, dec, mag, row(0,8)))
390 add++;
391 }
392
393 gLog << inf << "Read " << add << " out of " << n << " (Total max mag=" << maxmag << ")" << endl;
394
395 return add;
396}
397
398// --------------------------------------------------------------------------
399//
400// Read from a Bright Star Catalog catalog. set bit kHasChanged
401// Already read data is not deleted. To delete the stored data call
402// Delete().
403//
404Int_t MAstroCatalog::ReadBSC(TString catalog)
405{
406 gLog << inf << "Reading Bright Star Catalog (BSC5) catalog: " << catalog << endl;
407
408 ifstream fin(catalog);
409 if (!fin)
410 {
411 gLog << err << "Cannot open catalog file " << catalog << ": ";
412 gLog << strerror(errno) << endl;
413 return 0;
414 }
415
416 Int_t add=0;
417 Int_t cnt=0;
418 Int_t n =0;
419
420 Double_t maxmag=0;
421
422 while (1)
423 {
424 TString row;
425 row.ReadLine(fin);
426 if (!fin)
427 break;
428
429 cnt++;
430
431 const Int_t rah = atoi(row(75, 2));
432 const Int_t ram = atoi(row(77, 2));
433 const Float_t ras = atof(row(79, 4));
434 const Char_t decsgn = row(83);
435 const Int_t decd = atoi(row(84, 2));
436 const Int_t decm = atoi(row(86, 2));
437 const Int_t decs = atoi(row(88, 2));
438 const TString m = row(102, 5);
439
440 if (m.Strip().IsNull())
441 continue;
442
443 n++;
444
445 const Double_t mag = atof(m.Data());
446
447 maxmag = TMath::Max(maxmag, mag);
448
449 if (mag>fLimMag)
450 continue;
451
452 const Double_t ra = MAstro::Hms2Rad(rah, ram, ras);
453 const Double_t dec = MAstro::Dms2Rad(decd, decm, decs, decsgn);
454
455 if (AddObject(ra, dec, mag, row(4,9)))
456 add++;
457 }
458
459 gLog << inf << "Read " << add << " out of " << n << " (Total max mag=" << maxmag << ")" << endl;
460
461 return add;
462}
463
464// --------------------------------------------------------------------------
465//
466// Read from a ascii heasarc ppm catalog. set bit kHasChanged
467// Already read data is not deleted. To delete the stored data call
468// Delete().
469// If the second argument is given all survived stars are written
470// to a file outname. This files will contain an apropriate compressed
471// file format. You can read such files again using ReadCompressed.
472//
473// FIXME: A General loader for heasarc catlogs is missing, see
474// http://heasarc.gsfc.nasa.gov/W3Browse/star-catalog/
475//
476Int_t MAstroCatalog::ReadHeasarcPPM(TString catalog, TString outname)
477{
478 gLog << inf << "Reading Heasarc PPM catalog: " << catalog << endl;
479
480 ifstream fin(catalog);
481 if (!fin)
482 {
483 gLog << err << "Cannot open catalog file " << catalog << ": ";
484 gLog << strerror(errno) << endl;
485 return 0;
486 }
487
488 ofstream *fout = outname.IsNull() ? 0 : new ofstream(outname);
489 if (fout && !*fout)
490 {
491 gLog << warn << "Cannot open output file " << outname << ": ";
492 gLog << strerror(errno) << endl;
493 delete fout;
494 fout = 0;
495 }
496
497 Int_t add=0;
498 Int_t cnt=0;
499 Int_t n =0;
500
501 Double_t maxmag=0;
502
503 while (1)
504 {
505 TString row;
506 row.ReadLine(fin);
507 if (!fin)
508 break;
509
510 cnt++;
511
512 if (!row.BeginsWith("PPM "))
513 continue;
514
515 const TString name = row(0, row.First('|'));
516 row = row(row.First('|')+1, row.Length());
517 row = row(row.First('|')+1, row.Length());
518
519 const TString vmag = row(0, row.First('|'));
520
521 n++;
522 const Double_t mag = atof(vmag.Data());
523 maxmag = TMath::Max(maxmag, mag);
524 if (mag>fLimMag)
525 continue;
526
527 row = row(row.First('|')+1, row.Length());
528 row = row(row.First('|')+1, row.Length());
529
530 row = row(row.First('|')+1, row.Length());
531 row = row(row.First('|')+1, row.Length());
532
533 row = row(row.First('|')+1, row.Length());
534 row = row(row.First('|')+1, row.Length());
535
536 const TString ra = row(0, row.First('|'));
537 row = row(row.First('|')+1, row.Length());
538 const TString de = row(0, row.First('|'));
539 row = row(row.First('|')+1, row.Length());
540
541 Char_t sgn;
542 Int_t d, m;
543 Float_t s;
544 if (sscanf(ra.Data(), "%d %d %f", &d, &m, &s)!=3)
545 {
546 // gLog << "Error loading entry in line " << i << endl;
547 continue;
548 }
549 const Double_t ra0 = MAstro::Hms2Rad(d, m, s);
550
551 if (sscanf(de.Data(), "%c%d %d %f", &sgn, &d, &m, &s)!=4)
552 {
553 // gLog << "Error loading entry in line " << i << endl;
554 continue;
555 }
556 const Double_t de0 = MAstro::Dms2Rad(d, m, s, sgn);
557
558 if (!AddObject(ra0, de0, mag, name))
559 continue;
560
561 add++;
562
563 if (fout)
564 ((MVector3*)fList.Last())->WriteBinary(*fout);
565 }
566
567 gLog << inf << "Read " << add << " out of " << n << " (Total max mag=" << maxmag << ")" << endl;
568
569 return add;
570}
571
572// --------------------------------------------------------------------------
573//
574// Read from a MAstroCatalog compressed catalog. set bit kHasChanged
575// Already read data is not deleted. To delete the stored data call
576// Delete().
577//
578Int_t MAstroCatalog::ReadCompressed(TString catalog)
579{
580 SetBit(kHasChanged);
581
582 gLog << inf << "Reading MAstroCatalog compressed catalog: " << catalog << endl;
583
584 ifstream fin(catalog);
585 if (!fin)
586 {
587 gLog << err << "Cannot open catalog file " << catalog << ": ";
588 gLog << strerror(errno) << endl;
589 return 0;
590 }
591
592 Int_t add=0;
593 Int_t cnt=0;
594 Int_t n =0;
595
596 Double_t maxmag=0;
597
598 MVector3 entry;
599
600 while (1)
601 {
602 cnt++;
603
604 entry.ReadBinary(fin);
605 if (!fin)
606 break;
607
608 n++;
609
610 const Double_t mag = entry.Magnitude();
611 maxmag = TMath::Max(maxmag, mag);
612 if (mag>fLimMag)
613 continue;
614
615 if (entry.Angle(fRaDec)*TMath::RadToDeg()>fRadiusFOV)
616 continue;
617
618 fList.Add(entry.Clone());
619 add++;
620 }
621
622 gLog << inf << "Read " << add << " out of " << n << " (Total max mag=" << maxmag << ")" << endl;
623
624 return add;
625}
626
627// --------------------------------------------------------------------------
628//
629// Add an object to the star catalog manually. Return true if the object
630// was added and false otherwise (criteria is the FOV)
631//
632Bool_t MAstroCatalog::AddObject(Float_t ra, Float_t dec, Float_t mag, TString name)
633{
634 MVector3 *star = new MVector3;
635 star->SetRaDec(ra, dec, mag);
636 star->SetName(name);
637
638 if (star->Angle(fRaDec)*TMath::RadToDeg()>fRadiusFOV)
639 {
640 delete star;
641 return 0;
642 }
643
644 SetBit(kHasChanged);
645 fList.AddLast(star);
646 return 1;
647}
648
649// --------------------------------------------------------------------------
650//
651// Set Range of pad. If something has changed create and draw new primitives.
652// Paint all gui primitives.
653//
654void MAstroCatalog::Paint(Option_t *o)
655{
656 SetRangePad(o);
657
658 // In the case MAstroCatalog has been loaded from a file
659 // kHasChanged is not set, but fMapG.GetSize() is ==0
660 if (TestBit(kHasChanged) || fMapG.GetSize()==0)
661 DrawPrimitives(o);
662
663 fMapG.Paint();
664}
665
666// --------------------------------------------------------------------------
667//
668// Set Range of pad if pad available. If something has changed create
669// and draw new primitives. Paint all gui primitives to the Drawable with
670// Id id. This can be used to be able to
671//
672/*
673void MAstroCatalog::PaintImg(Int_t id, Option_t *o)
674{
675 if (gPad)
676 SetRangePad(o);
677
678 if (TestBit(kHasChanged))
679 {
680 if (id>0)
681 gPad=0;
682 DrawPrimitives(o);
683 }
684
685 fMapG.Paint(id, fRadiusFOV);
686}
687*/
688
689// --------------------------------------------------------------------------
690//
691// Set Range of pad. If something has changed create and draw new primitives.
692// Paint all gui primitives.
693//
694// Because in some kind of multi-threaded environments gPad doesn't stay
695// the same in a single thread (because it might be changed in the same
696// thread inside a gui updating timer for example) we have to secure the
697// usage of gPad with a bit. This is also not multi-thread safe against
698// calling this function, but the function should work well in multi-
699// threaded environments. Never call this function from different threads
700// simultaneously.
701//
702void MAstroCatalog::PaintImg(unsigned char *buf, int w, int h, Option_t *o)
703{
704 if (!o)
705 o = "local mirrorx yellow * =";
706
707 if (TestBit(kHasChanged))
708 {
709 SetBit(kDrawingImage);
710 DrawPrimitives(o);
711 ResetBit(kDrawingImage);
712 }
713
714 fMapG.Paint(buf, w, h, fRadiusFOV);
715}
716
717// --------------------------------------------------------------------------
718//
719// Draw a black marker at the position of the star. Create a corresponding
720// tooltip with the coordinates.
721// x, y: Pad Coordinates to draw star
722// v: Sky position (Ra/Dec) of the star
723// col: Color of marker (<0 mean transparent)
724// txt: additional tooltip text
725// resize: means resize the marker according to the magnitude
726//
727void MAstroCatalog::DrawStar(Double_t x, Double_t y, const TVector3 &v, Int_t col, const char *txt, Bool_t resize)
728{
729 const Double_t ra = v.Phi()*TMath::RadToDeg()/15;
730 const Double_t dec = (TMath::Pi()/2-v.Theta())*TMath::RadToDeg();
731
732 const Double_t mag = -2.5*log10(v.Mag());
733
734 TString str(v.GetName());
735 if (!str.IsNull())
736 str += ": ";
737 str += MString::Form("Ra=%.2fh Dec=%.1fd Mag=%.1f", ra, dec, mag);
738 if (txt)
739 {
740 str += " (";
741 str += txt;
742 str += ")";
743 }
744
745 // draw star on the camera display
746 TMarker *tip=new TMarker(x, y, kDot);
747 TAttMarker::Copy(*tip);
748
749 fMapG.Add(tip, new TString(str));
750
751 if (resize)
752 tip->SetMarkerSize((10 - (mag>1 ? mag : 1))/15);
753}
754
755// --------------------------------------------------------------------------
756//
757// Set pad as modified.
758//
759void MAstroCatalog::Update(Bool_t upd)
760{
761 SetBit(kHasChanged);
762 if (gPad && TestBit(kMustCleanup))
763 {
764 gPad->Modified();
765 if (upd)
766 gPad->Update();
767 }
768}
769
770// --------------------------------------------------------------------------
771//
772// Set the observation time. Necessary to use local coordinate
773// system. The MTime object is cloned.
774//
775void MAstroCatalog::SetTime(const MTime &time)
776{
777 if (fTime)
778 delete fTime;
779 fTime=(MTime*)time.Clone();
780}
781
782// --------------------------------------------------------------------------
783//
784// Set the observatory location. Necessary to use local coordinate
785// system. The MObservatory object is cloned.
786//
787void MAstroCatalog::SetObservatory(const MObservatory &obs)
788{
789 if (fObservatory)
790 delete fObservatory;
791 fObservatory=new MObservatory;
792 obs.Copy(*fObservatory);
793}
794
795// --------------------------------------------------------------------------
796//
797// Convert the vector to pad coordinates. After conversion
798// the x- coordinate of the vector must be the x coordinate
799// of the pad - the same for y. If the coordinate is inside
800// the current draw area return kTRUE, otherwise kFALSE.
801// If it is an invalid coordinate return kERROR
802//
803Int_t MAstroCatalog::ConvertToPad(const TVector3 &w0, TVector2 &v) const
804{
805 TVector3 w(w0);
806
807 // Stretch such, that the Z-component is alwas the same. Now
808 // X and Y contains the intersection point between the star-light
809 // and the plain of a virtual plain screen (ccd...)
810 if (TestBit(kPlainScreen))
811 w *= 1./w(2);
812
813 w *= TMath::RadToDeg(); // FIXME: *conversion factor?
814 v.Set(TestBit(kMirrorX) ? -w(0) : w(0),
815 TestBit(kMirrorY) ? -w(1) : w(1));
816
817 v=v.Rotate(fAngle*TMath::DegToRad());
818
819 if (w(2)<0)
820 return kERROR;
821
822 if (TestBit(kDrawingImage) || !gPad)
823 return v.Mod2()<fRadiusFOV*fRadiusFOV;
824
825 return v.X()>gPad->GetX1() && v.Y()>gPad->GetY1() &&
826 v.X()<gPad->GetX2() && v.Y()<gPad->GetY2();
827}
828
829// --------------------------------------------------------------------------
830//
831// Convert theta/phi coordinates of v by TRotation into new coordinate
832// system and convert the coordinated to pad by ConvertToPad.
833// The result is retunred in v.
834//
835Int_t MAstroCatalog::Convert(const TRotation &rot, TVector2 &v) const
836{
837 MVector3 w;
838 w.SetMagThetaPhi(1, v.Y(), v.X());
839 w *= rot;
840
841 return ConvertToPad(w, v);
842}
843
844// --------------------------------------------------------------------------
845//
846// Draw a line from v to v+(dx,dy) using Convert/ConvertToPad to get the
847// corresponding pad coordinates.
848//
849Bool_t MAstroCatalog::DrawLine(const TVector2 &v, Int_t dx, Int_t dy, const TRotation &rot, Int_t type)
850{
851 const TVector2 add(dx*TMath::DegToRad(), dy*TMath::DegToRad());
852
853 // Define all lines in the same direction
854 const TVector2 va(dy==1?v:v+add);
855 const TVector2 vb(dy==1?v+add:v);
856
857 TVector2 v0(va);
858 TVector2 v1(vb);
859
860 const Int_t rc0 = Convert(rot, v0);
861 const Int_t rc1 = Convert(rot, v1);
862
863 // Both are kFALSE or both are kERROR
864 if ((rc0|rc1)==kFALSE || (rc0&rc1)==kERROR)
865 return kFALSE;
866
867 TLine *line = new TLine(v0.X(), v0.Y(), v1.X(), v1.Y());
868 if (type==1)
869 dynamic_cast<TAttLine&>(fAttLineSky).Copy(dynamic_cast<TAttLine&>(*line));
870 else
871 dynamic_cast<TAttLine&>(fAttLineLocal).Copy(dynamic_cast<TAttLine&>(*line));
872 fMapG.Add(line);
873
874 if (dx!=0)
875 return kTRUE;
876
877 const TVector2 deg = va*TMath::RadToDeg();
878
879 MString txt;
880 if (type==1)
881 txt.Print("Ra=%.2fh Dec=%.1fd", fmod(deg.X()/15+48, 24), fmod(90-deg.Y()+270,180)-90);
882 else
883 txt.Print("Zd=%.1fd Az=%.1fd", fmod(deg.Y()+270,180)-90, fmod(deg.X()+720, 360));
884
885 TMarker *tip=new TMarker(v0.X(), v0.Y(), kDot);
886 tip->SetMarkerColor(kWhite+type*2);
887 fMapG.Add(tip, new TString(txt));
888
889 return kTRUE;
890}
891
892// --------------------------------------------------------------------------
893//
894// Use "local" draw option to align the display to the local
895// coordinate system instead of the sky coordinate system.
896// dx, dy are arrays storing recuresively all touched points
897// stepx, stepy are the step-size of the current grid.
898//
899void MAstroCatalog::Draw(const TVector2 &v0, const TRotation &rot, TArrayI &dx, TArrayI &dy, Int_t stepx, Int_t stepy, Int_t type)
900{
901 // Calculate the end point
902 const TVector2 v1 = v0 + TVector2(dx[0]*TMath::DegToRad(), dy[0]*TMath::DegToRad());
903
904 // Check whether the point has already been touched.
905 Int_t idx[] = {1, 1, 1, 1};
906
907 Int_t dirs[4][2] = { {0, stepy}, {stepx, 0}, {0, -stepy}, {-stepx, 0} };
908
909 // Check for ambiguities.
910 for (int i=0; i<dx.GetSize(); i++)
911 {
912 for (int j=0; j<4; j++)
913 {
914 const Bool_t rcx0 = (dx[i]+720)%360==(dx[0]+dirs[j][0]+720)%360;
915 const Bool_t rcy0 = (dy[i]+360)%180==(dy[0]+dirs[j][1]+360)%180;
916 if (rcx0&&rcy0)
917 idx[j] = 0;
918 }
919 }
920
921 // Enhance size of array by 1, copy current
922 // position as last entry
923 dx.Set(dx.GetSize()+1);
924 dy.Set(dy.GetSize()+1);
925
926 dx[dx.GetSize()-1] = dx[0];
927 dy[dy.GetSize()-1] = dy[0];
928
929 // Store current positon
930 const Int_t d[2] = { dx[0], dy[0] };
931
932 for (int i=0; i<4; i++)
933 if (idx[i])
934 {
935 // Calculate new position
936 dx[0] = d[0]+dirs[i][0];
937 dy[0] = d[1]+dirs[i][1];
938
939 // Draw corresponding line and iterate through grid
940 if (DrawLine(v1, dirs[i][0], dirs[i][1], rot, type))
941 Draw(v0, rot, dx, dy, stepx, stepy, type);
942
943 dx[0]=d[0];
944 dy[0]=d[1];
945 }
946}
947
948// --------------------------------------------------------------------------
949//
950// Draw a grid recursively around the point v0 (either Ra/Dec or Zd/Az)
951// The points in the grid are converted by a TRotation and CovertToPad
952// to pad coordinates. The type arguemnts is neccessary to create the
953// correct tooltip (Ra/Dec, Zd/Az) at the grid-points.
954// From the pointing position the step-size of teh gris is caluclated.
955//
956void MAstroCatalog::DrawGrid(const TVector3 &v0, const TRotation &rot, Int_t type)
957{
958 TArrayI dx(1);
959 TArrayI dy(1);
960
961 // align to 1deg boundary
962 TVector2 v(v0.Phi()*TMath::RadToDeg(), v0.Theta()*TMath::RadToDeg());
963 v.Set((Float_t)TMath::Nint(v.X()), (Float_t)TMath::Nint(v.Y()));
964
965 // calculate stepsizes based on visible FOV
966 Int_t stepx = 1;
967
968 if (v.Y()<fRadiusFOV || v.Y()>180-fRadiusFOV)
969 stepx=36;
970 else
971 {
972 // This is a rough estimate how many degrees are visible
973 const Float_t m = log(fRadiusFOV/180.)/log(90./(fRadiusFOV+1)+1);
974 const Float_t t = log(180.)-m*log(fRadiusFOV);
975 const Float_t f = m*log(90-fabs(90-v.Y()))+t;
976 const Int_t nx = (Int_t)(exp(f)+0.5);
977 stepx = nx<4 ? 1 : nx/4;
978 if (stepx>36)
979 stepx=36;
980 }
981
982 const Int_t ny = (Int_t)(fRadiusFOV+1);
983 Int_t stepy = ny<4 ? 1 : ny/4;
984
985 // align stepsizes to be devisor or 180 and 90
986 while (180%stepx)
987 stepx++;
988 while (90%stepy)
989 stepy++;
990
991 // align to step-size boundary (search for the nearest one)
992 Int_t dv = 1;
993 while ((int)(v.X())%stepx)
994 {
995 v.Set(v.X()+dv, v.Y());
996 dv = -TMath::Sign(TMath::Abs(dv)+1, dv);
997 }
998
999 dv = 1;
1000 while ((int)(v.Y())%stepy)
1001 {
1002 v.Set(v.X(), v.Y()+dv);
1003 dv = -TMath::Sign(TMath::Abs(dv)+1, dv);
1004 }
1005
1006 // draw...
1007 v *= TMath::DegToRad();
1008
1009 Draw(v, rot, dx, dy, stepx, stepy, type);
1010}
1011
1012// --------------------------------------------------------------------------
1013//
1014// Get a rotation matrix which aligns the pointing position
1015// to the center of the x,y plain
1016//
1017TRotation MAstroCatalog::AlignCoordinates(const TVector3 &v) const
1018{
1019 TRotation trans;
1020 trans.RotateZ(-v.Phi());
1021 trans.RotateY(-v.Theta());
1022 trans.RotateZ(-TMath::Pi()/2);
1023 return trans;
1024}
1025
1026// --------------------------------------------------------------------------
1027//
1028// Return the rotation matrix which converts either sky or
1029// local coordinates to coordinates which pole is the current
1030// pointing direction.
1031//
1032TRotation MAstroCatalog::GetGrid(Bool_t local)
1033{
1034 const Bool_t enable = fTime && fObservatory;
1035
1036 // If sky coordinate view is requested get rotation matrix and
1037 // draw corresponding sky-grid and if possible local grid
1038 if (!local)
1039 {
1040 const TRotation trans(AlignCoordinates(fRaDec));
1041
1042 DrawGrid(fRaDec, trans, 1);
1043
1044 if (enable)
1045 {
1046 const MAstroSky2Local rot(*fTime, *fObservatory);
1047 DrawGrid(rot*fRaDec, trans*rot.Inverse(), 2);
1048 }
1049
1050 // Return the correct rotation matrix
1051 return trans;
1052 }
1053
1054 // If local coordinate view is requested get rotation matrix and
1055 // draw corresponding sky-grid and if possible local grid
1056 if (local && enable)
1057 {
1058 const MAstroSky2Local rot(*fTime, *fObservatory);
1059
1060 const TRotation trans(AlignCoordinates(rot*fRaDec));
1061
1062 DrawGrid(fRaDec, trans*rot, 1);
1063 DrawGrid(rot*fRaDec, trans, 2);
1064
1065 // Return the correct rotation matrix
1066 return trans*rot;
1067 }
1068
1069 return TRotation();
1070}
1071
1072// --------------------------------------------------------------------------
1073//
1074// Create the title for the pad.
1075//
1076TString MAstroCatalog::GetPadTitle() const
1077{
1078 const Double_t ra = fRaDec.Phi()*TMath::RadToDeg();
1079 const Double_t dec = (TMath::Pi()/2-fRaDec.Theta())*TMath::RadToDeg();
1080
1081 TString txt;
1082 txt += Form("\\alpha=%.2fh ", fmod(ra/15+48, 24));
1083 txt += Form("\\delta=%.1f\\circ ", fmod(dec+270,180)-90);
1084 txt += Form("/ FOV=%.1f\\circ", fRadiusFOV);
1085
1086 if (!fTime || !fObservatory)
1087 return txt;
1088
1089 const MAstroSky2Local rot(*fTime, *fObservatory);
1090 const TVector3 loc = rot*fRaDec;
1091
1092 const Double_t rho = rot.RotationAngle(fRaDec.Phi(), TMath::Pi()/2-fRaDec.Theta());
1093
1094 const Double_t zd = TMath::RadToDeg()*loc.Theta();
1095 const Double_t az = TMath::RadToDeg()*loc.Phi();
1096
1097 txt.Prepend("#splitline{");
1098 txt += Form(" \\theta=%.1f\\circ ", fmod(zd+270,180)-90);
1099 txt += Form("\\phi=%.1f\\circ ", fmod(az+720, 360));
1100 txt += Form(" / \\rho=%.1f\\circ", rho*TMath::RadToDeg());
1101 txt += "}{<";
1102 txt += fTime->GetSqlDateTime();
1103 txt += ">}";
1104 return txt;
1105}
1106
1107// --------------------------------------------------------------------------
1108//
1109// To overlay the catalog make sure, that in any case you are using
1110// the 'same' option.
1111//
1112// If you want to overlay this on top of any picture which is created
1113// by derotation of the camera plain you have to use the 'mirror' option
1114// the compensate the mirroring of the image in the camera plain.
1115//
1116// If you have already compensated this by x=-x and y=-y when creating
1117// the histogram you can simply overlay the catalog.
1118//
1119// To overlay the catalog on a 2D histogram the histogram must have
1120// units of degrees (which are plain, like you directly convert the
1121// camera units by multiplication to degrees)
1122//
1123// To be 100% exact you must use the option 'plain' which assumes a plain
1124// screen. This is not necessary for the MAGIC-camera because the
1125// difference between both is less than 1e-3.
1126//
1127// You should always be aware of the fact, that the shown stars and the
1128// displayed grid is the ideal case, like a reflection on a virtual
1129// perfectly aligned central mirror. In reality the star-positions are
1130// smeared to the edge of the camera the more the distance to the center
1131// is, such that the center of gravity of the light distribution might
1132// be more far away from the center than the display shows.
1133//
1134// If you want the stars to be displayed as circles with a size
1135// showing their magnitude use "*" as an option.
1136//
1137// Use 'white' to display white instead of black stars
1138// Use 'yellow' to display white instead of black stars
1139//
1140//
1141void MAstroCatalog::AddPrimitives(TString o)
1142{
1143 const Bool_t same = o.Contains("same", TString::kIgnoreCase);
1144 const Bool_t local = o.Contains("local", TString::kIgnoreCase);
1145 const Bool_t mirx = o.Contains("mirrorx", TString::kIgnoreCase);
1146 const Bool_t miry = o.Contains("mirrory", TString::kIgnoreCase);
1147 const Bool_t mirror = o.Contains("mirror", TString::kIgnoreCase) && !mirx && !miry;
1148 const Bool_t size = o.Contains("*", TString::kIgnoreCase);
1149 const Bool_t white = o.Contains("white", TString::kIgnoreCase);
1150 const Bool_t yellow = o.Contains("yellow", TString::kIgnoreCase) && !white;
1151 const Bool_t rot180 = o.Contains("180", TString::kIgnoreCase);
1152 const Bool_t rot270 = o.Contains("270", TString::kIgnoreCase);
1153 const Bool_t rot90 = o.Contains("90", TString::kIgnoreCase);
1154
1155 if (white)
1156 SetMarkerColor(kWhite);
1157
1158 fAngle = 0;
1159 if (rot90)
1160 fAngle=90;
1161 if (rot180)
1162 fAngle=180;
1163 if (rot270)
1164 fAngle=270;
1165
1166 // X is vice versa, because ra is defined anti-clockwise
1167 mirx || mirror ? ResetBit(kMirrorX) : SetBit(kMirrorX);
1168 miry || mirror ? SetBit(kMirrorY) : ResetBit(kMirrorY);
1169
1170 const TRotation rot(GetGrid(local));
1171
1172 TIter Next(&fList);
1173 MVector3 *v=0;
1174 while ((v=(MVector3*)Next()))
1175 {
1176 if (v->Magnitude()>fLimMag)
1177 continue;
1178
1179 TVector2 s(v->Phi(), v->Theta());
1180 if (Convert(rot, s)==kTRUE)
1181 DrawStar(s.X(), s.Y(), *v, yellow?kYellow:(white?kWhite:kBlack), 0, size);
1182 }
1183
1184 if (!same && !TestBit(kDrawingImage) && gPad)
1185 {
1186 TPaveText *pv = new TPaveText(0.01, 0.90, 0.63, 0.99, "brNDC");
1187 pv->AddText(GetPadTitle());
1188 fMapG.Add(pv);
1189 }
1190
1191 TMarker *mk=new TMarker(0, 0, kMultiply);
1192 mk->SetMarkerColor(white||yellow?kWhite:kBlack);
1193 mk->SetMarkerSize(1.5);
1194 fMapG.Add(mk);
1195}
1196
1197// --------------------------------------------------------------------------
1198//
1199// Do nothing if 'same' option given.
1200// Otherwise set pad-range such that x- and y- coordinates have the same
1201// step-size
1202//
1203void MAstroCatalog::SetRangePad(Option_t *o)
1204{
1205 if (TString(o).Contains("same", TString::kIgnoreCase))
1206 return;
1207
1208 const Double_t edge = fRadiusFOV/TMath::Sqrt(2.);
1209 //gPad->Range(-edge, -edge, edge, edge);
1210
1211 const Float_t w = gPad->GetWw();
1212 const Float_t h = gPad->GetWh();
1213
1214 if (w<h)
1215 gPad->Range(-edge, -edge*h/w, edge, edge*h/w);
1216 else
1217 gPad->Range(-edge*w/h, -edge, edge*w/h, edge);
1218}
1219
1220// --------------------------------------------------------------------------
1221//
1222// Bends some pointers into the right direction...
1223// Calls TAttLine::SetLineAttributes and connects some signals
1224// to the gui to recreate the gui elements if something has changed.
1225//
1226void MAstroCatalog::SetLineAttributes(MAttLine &att)
1227{
1228 if (!gPad)
1229 return;
1230
1231 gPad->SetSelected(&att);
1232 gROOT->SetSelectedPrimitive(&att);
1233
1234 att.SetLineAttributes();
1235
1236 TQObject::Connect("TGColorSelect", "ColorSelected(Pixel_t)", "MAstroCatalog", this, "ForceUpdate()");
1237 TQObject::Connect("TGListBox", "Selected(Int_t)", "MAstroCatalog", this, "ForceUpdate()");
1238}
1239
1240// --------------------------------------------------------------------------
1241//
1242// Calls TAttMarker::SetMarkerAttributes and connects some signals
1243// to the gui to recreate the gui elements if something has changed.
1244//
1245void MAstroCatalog::SetMarkerAttributes()
1246{
1247 if (!gPad)
1248 return;
1249
1250 TAttMarker::SetMarkerAttributes();
1251
1252 // Make sure that if something is changed the gui elements
1253 // are recreated
1254 TQObject::Connect("TGedMarkerSelect", "MarkerSelected(Style_t)", "MAstroCatalog", this, "ForceUpdate()");
1255 TQObject::Connect("TGColorSelect", "ColorSelected(Pixel_t)", "MAstroCatalog", this, "ForceUpdate()");
1256 TQObject::Connect("TGListBox", "Selected(Int_t)", "MAstroCatalog", this, "ForceUpdate()");
1257}
1258
1259void MAstroCatalog::DrawPrimitives(Option_t *o)
1260{
1261 fMapG.Delete();
1262
1263 if (!TestBit(kDrawingImage) && gPad)
1264 SetRangePad(o);
1265
1266#ifdef DEBUG
1267 TStopwatch clk;
1268 clk.Start();
1269#endif
1270 AddPrimitives(o);
1271#ifdef DEBUG
1272 clk.Stop();
1273 clk.Print();
1274#endif
1275
1276 // Append to a possible second pad
1277 if (!TestBit(kDrawingImage) && gPad && !gPad->GetListOfPrimitives()->FindObject(this))
1278 AppendPad(o);
1279
1280 ResetBit(kHasChanged);
1281}
1282
1283// --------------------------------------------------------------------------
1284//
1285// Append "this" to current pad
1286// set bit kHasChanged to recreate all gui elements
1287// Connect signal
1288//
1289void MAstroCatalog::Draw(Option_t *o)
1290{
1291 // Append to first pad
1292 AppendPad(o);
1293
1294 // If contents have not previously changed make sure that
1295 // all primitives are recreated.
1296 SetBit(kHasChanged);
1297
1298 // Connect all TCanvas::ProcessedEvent to this->EventInfo
1299 // This means, that after TCanvas has processed an event
1300 // EventInfo of this class is called, see TCanvas::HandleInput
1301 gPad->GetCanvas()->Connect("ProcessedEvent(Int_t,Int_t,Int_t,TObject*)",
1302 "MAstroCatalog", this,
1303 "EventInfo(Int_t,Int_t,Int_t,TObject*)");
1304}
1305
1306// --------------------------------------------------------------------------
1307//
1308// This function was connected to all created canvases. It is used
1309// to redirect GetObjectInfo into our own status bar.
1310//
1311// The 'connection' is done in Draw. It seems that 'connected'
1312// functions must be public.
1313//
1314void MAstroCatalog::EventInfo(Int_t event, Int_t px, Int_t py, TObject *selected)
1315{
1316 TCanvas *c = (TCanvas*)gTQSender;
1317
1318 gPad = c ? c->GetSelectedPad() : NULL;
1319 if (!gPad)
1320 return;
1321
1322
1323 // Try to find a corresponding object with kCannotPick set and
1324 // an available TString (for a tool tip)
1325 TString str;
1326 if (!selected || selected==this)
1327 selected = fMapG.PickObject(px, py, str);
1328
1329 if (!selected)
1330 return;
1331
1332 // Handle some gui events
1333 switch (event)
1334 {
1335 case kMouseMotion:
1336 if (fToolTip && !fToolTip->IsMapped() && !str.IsNull())
1337 ShowToolTip(px, py, str);
1338 break;
1339
1340 case kMouseLeave:
1341 if (fToolTip && fToolTip->IsMapped())
1342 fToolTip->Hide();
1343 break;
1344
1345 case kKeyPress:
1346 ExecuteEvent(kKeyPress, px, py);
1347 break;
1348 }
1349}
1350
1351// --------------------------------------------------------------------------
1352//
1353// Handle keyboard events.
1354//
1355void MAstroCatalog::ExecuteEventKbd(Int_t keycode, Int_t keysym)
1356{
1357 Double_t dra =0;
1358 Double_t ddec=0;
1359
1360 switch (keysym)
1361 {
1362 case kKey_Left:
1363 dra = -TMath::DegToRad();
1364 break;
1365 case kKey_Right:
1366 dra = +TMath::DegToRad();
1367 break;
1368 case kKey_Up:
1369 ddec = +TMath::DegToRad();
1370 break;
1371 case kKey_Down:
1372 ddec = -TMath::DegToRad();
1373 break;
1374 case kKey_Plus:
1375 SetRadiusFOV(fRadiusFOV+1);
1376 break;
1377 case kKey_Minus:
1378 SetRadiusFOV(fRadiusFOV-1);
1379 break;
1380
1381 default:
1382 return;
1383 }
1384
1385 const Double_t r = fRaDec.Phi();
1386 const Double_t d = TMath::Pi()/2-fRaDec.Theta();
1387
1388 SetRaDec(r+dra, d+ddec);
1389
1390 gPad->Update();
1391}
1392
1393// ------------------------------------------------------------------------
1394//
1395// Execute a gui event on the camera
1396//
1397void MAstroCatalog::ExecuteEvent(Int_t event, Int_t mp1, Int_t mp2)
1398{
1399 if (!TestBit(kGuiActive))
1400 return;
1401
1402 if (event==kKeyPress)
1403 ExecuteEventKbd(mp1, mp2);
1404}
1405
1406// --------------------------------------------------------------------------
1407//
1408// Displays a tooltip
1409//
1410void MAstroCatalog::ShowToolTip(Int_t px, Int_t py, const char *txt)
1411{
1412 if (TestBit(kNoToolTips))
1413 return;
1414
1415 Int_t x=0;
1416 Int_t y=0;
1417
1418 const Window_t id1 = gVirtualX->GetWindowID(gPad->GetCanvasID());
1419 const Window_t id2 = fToolTip->GetParent()->GetId();
1420
1421 Window_t id3;
1422 gVirtualX->TranslateCoordinates(id1, id2, px, py, x, y, id3);
1423
1424 // Show tool tip
1425 fToolTip->SetText(txt);
1426 fToolTip->Show(x+4, y+4);
1427}
1428
1429// --------------------------------------------------------------------------
1430//
1431// Calculate distance to primitive by checking all gui elements
1432//
1433Int_t MAstroCatalog::DistancetoPrimitive(Int_t px, Int_t py)
1434{
1435 return fMapG.DistancetoPrimitive(px, py);
1436}
1437
1438// ------------------------------------------------------------------------
1439//
1440// Returns string containing info about the object at position (px,py).
1441// Returned string will be re-used (lock in MT environment).
1442//
1443char *MAstroCatalog::GetObjectInfo(Int_t px, Int_t py) const
1444{
1445 return fMapG.GetObjectInfo(px, py);
1446}
Note: See TracBrowser for help on using the repository browser.