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

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