GammaLib  2.1.0.dev
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
GSkyRegions.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  * GSkyRegions.cpp - Sky region container class *
3  * ----------------------------------------------------------------------- *
4  * copyright (C) 2013-2021 by Pierrick Martin *
5  * ----------------------------------------------------------------------- *
6  * *
7  * This program is free software: you can redistribute it and/or modify *
8  * it under the terms of the GNU General Public License as published by *
9  * the Free Software Foundation, either version 3 of the License, or *
10  * (at your option) any later version. *
11  * *
12  * This program is distributed in the hope that it will be useful, *
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of *
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
15  * GNU General Public License for more details. *
16  * *
17  * You should have received a copy of the GNU General Public License *
18  * along with this program. If not, see <http://www.gnu.org/licenses/>. *
19  * *
20  ***************************************************************************/
21 /**
22 * @file GSkyRegions.cpp
23 * @brief Sky regions container class definition
24 * @author Pierrick Martin
25 */
26 
27 /* __ Includes ___________________________________________________________ */
28 #ifdef HAVE_CONFIG_H
29 #include <config.h>
30 #include <fstream>
31 #endif
32 #include "GBase.hpp"
33 #include "GTools.hpp"
34 #include "GSkyDir.hpp"
35 #include "GSkyRegion.hpp"
36 #include "GSkyRegionCircle.hpp"
37 #include "GSkyRegionRectangle.hpp"
38 #include "GSkyRegions.hpp"
39 
40 /* __ Method name definitions ____________________________________________ */
41 #define G_ACCESS "GSkyRegions::operator[](std::string&)"
42 #define G_AT "GSkyRegions::at(int&)"
43 #define G_SET1 "GSkyRegions::set(int&, GSkyRegion&)"
44 #define G_SET2 "GSkyRegions::set(std::string&, GSkyRegion&)"
45 #define G_APPEND "GSkyRegions::append(GSkyRegion&)"
46 #define G_INSERT1 "GSkyRegions::insert(int&, GSkyRegion&)"
47 #define G_INSERT2 "GSkyRegions::insert(std::string&, GSkyRegion&)"
48 #define G_REMOVE1 "GSkyRegions::remove(int&)"
49 #define G_REMOVE2 "GSkyRegions::remove(std::string&)"
50 #define G_EXTEND "GSkyRegions::extend(GSkyRegions&)"
51 #define G_LOAD "GSkyRegions::load(GFilename&)"
52 #define G_SAVE "GSkyRegions::save(GFilename&)"
53 
54 /* __ Macros _____________________________________________________________ */
55 
56 /* __ Coding definitions _________________________________________________ */
57 
58 /* __ Debug definitions __________________________________________________ */
59 
60 
61 /*==========================================================================
62  = =
63  = Constructors/destructors =
64  = =
65  ==========================================================================*/
66 
67 /***********************************************************************//**
68  * @brief Void constructor
69  ***************************************************************************/
71 {
72  // Initialise members
73  init_members();
74 
75  // Return
76  return;
77 }
78 
79 
80 /***********************************************************************//**
81  * @brief Copy constructor
82  *
83  * @param[in] regions region container.
84  ***************************************************************************/
86 {
87  // Initialise members
88  init_members();
89 
90  // Copy members
91  copy_members(regions);
92 
93  // Return
94  return;
95 }
96 
97 
98 /***********************************************************************//**
99  * @brief Load constructor
100  *
101  * @param[in] filename DS9 region file.
102  *
103  * Constructs region container from a DS9 region file.
104  ***************************************************************************/
106 {
107  // Initialise members
108  init_members();
109 
110  // Load XML file
111  load(filename);
112 
113  // Return
114  return;
115 }
116 
117 
118 /***********************************************************************//**
119  * @brief Destructor
120  ***************************************************************************/
122 {
123  // Free members
124  free_members();
125 
126  // Return
127  return;
128 }
129 
130 
131 /*==========================================================================
132  = =
133  = Operators =
134  = =
135  ==========================================================================*/
136 
137 /***********************************************************************//**
138  * @brief Assignment operator
139  *
140  * @param[in] regions region container.
141  * @return region container.
142  ***************************************************************************/
144 {
145  // Execute only if object is not identical
146  if (this != &regions) {
147 
148  // Free members
149  free_members();
150 
151  // Initialise members
152  init_members();
153 
154  // Copy members
155  copy_members(regions);
156 
157  } // endif: object was not identical
158 
159  // Return
160  return *this;
161 }
162 
163 
164 /*==========================================================================
165  = =
166  = Public methods =
167  = =
168  ==========================================================================*/
169 
170 /***********************************************************************//**
171  * @brief Clear object
172  *
173  * Removes all regions from the container.
174  ***************************************************************************/
176 {
177  // Free class members
178  free_members();
179 
180  // Initialise members
181  init_members();
182 
183  // Return
184  return;
185 }
186 
187 
188 /***********************************************************************//**
189  * @brief Clone instance
190  *
191  * @return Pointer to deep copy of region container
192  *
193  * Makes a deep copy of the region container instance.
194  ***************************************************************************/
196 {
197  return new GSkyRegions(*this);
198 }
199 
200 
201 /***********************************************************************//**
202  * @brief Return pointer to region
203  *
204  * @param[in] index Sky region index [0,...,size()[.
205  *
206  * @exception GException::out_of_range
207  * Sky region index is out of range.
208  *
209  * Returns a pointer to the region with the specified @p index.
210  ***************************************************************************/
211 GSkyRegion* GSkyRegions::at(const int& index)
212 {
213  // Throw exception if index is out of range
214  if (index < 0 || index >= size()) {
215  throw GException::out_of_range(G_AT, "Sky region index",
216  index, size());
217  }
218 
219  // Return pointer
220  return m_regions[index];
221 }
222 
223 
224 /***********************************************************************//**
225  * @brief Return pointer to region (const version)
226  *
227  * @param[in] index Sky region index [0,...,size()[.
228  *
229  * @exception GException::out_of_range
230  * Sky region index is out of range.
231  *
232  * Returns a const pointer to the region with the specified @p index.
233  ***************************************************************************/
234 const GSkyRegion* GSkyRegions::at(const int& index) const
235 {
236  // Raise exception if index is out of range
237  if (index < 0 || index >= size()) {
238  throw GException::out_of_range(G_AT, "Sky Region index",
239  index, size());
240  }
241 
242  // Return pointer
243  return m_regions[index];
244 }
245 
246 
247 /***********************************************************************//**
248  * @brief Set region in container
249  *
250  * @param[in] index Sky region index [0,...,size()[.
251  * @param[in] region Sky region.
252  * @return Pointer to deep copy of sky region.
253  *
254  * @exception GException::out_of_range
255  * Sky region index is out of range.
256  * @exception GException::invalid_value
257  * Name of region exists already in container.
258  *
259  * Set sky region in the container. A deep copy of the region will be made.
260  ***************************************************************************/
261 GSkyRegion* GSkyRegions::set(const int& index, const GSkyRegion& region)
262 {
263  // Compile option: raise exception if index is out of range
264  #if defined(G_RANGE_CHECK)
265  if (index < 0 || index >= size()) {
266  throw GException::out_of_range(G_SET1, "Sky Region index",
267  index, size());
268  }
269  #endif
270 
271  // Free existing region only if it differs from current region. This
272  // prevents unintential deallocation of the argument
273  if ((m_regions[index] != NULL) && (m_regions[index] != &region)) {
274  delete m_regions[index];
275  }
276 
277  // Assign new region by cloning
278  m_regions[index] = region.clone();
279 
280  // Return pointer to region
281  return m_regions[index];
282 }
283 
284 
285 /***********************************************************************//**
286  * @brief Append region to container
287  *
288  * @param[in] region region.
289  * @return Pointer to deep copy of region.
290  *
291  * @exception GException::invalid_value
292  * Name of region exists already in container.
293  *
294  * Appends region to the container by making a deep copy of the region and
295  * storing its pointer.
296  ***************************************************************************/
298 {
299  // Create deep copy of region
300  GSkyRegion* ptr = region.clone();
301 
302  // Append deep copy of region
303  m_regions.push_back(ptr);
304 
305  // Return pointer to region
306  return ptr;
307 }
308 
309 
310 /***********************************************************************//**
311  * @brief Insert region into container
312  *
313  * @param[in] index Sky region index [0,...,size()[.
314  * @param[in] region Sky region.
315  * @return Pointer to deep copy of sky region.
316  *
317  * @exception GException::out_of_range
318  * Sky region index is out of range.
319  * @exception GException::invalid_value
320  * Name of region exists already in container.
321  *
322  * Inserts a @p region into the container before the region with the specified
323  * @p index.
324  ***************************************************************************/
325 GSkyRegion* GSkyRegions::insert(const int& index, const GSkyRegion& region)
326 {
327  // Compile option: raise exception if index is out of range
328  #if defined(G_RANGE_CHECK)
329  if (is_empty()) {
330  if (index > 0) {
331  throw GException::out_of_range(G_INSERT1, "Sky Region index",
332  index, size());
333  }
334  }
335  else {
336  if (index < 0 || index >= size()) {
337  throw GException::out_of_range(G_INSERT1, "Sky Region index",
338  index, size());
339  }
340  }
341  #endif
342 
343  // Create deep copy of region
344  GSkyRegion* ptr = region.clone();
345 
346  // Inserts deep copy of region
347  m_regions.insert(m_regions.begin()+index, ptr);
348 
349  // Return pointer to region
350  return ptr;
351 }
352 
353 
354 /***********************************************************************//**
355  * @brief Remove region from container
356  *
357  * @param[in] index Sky region index [0,...,size()[.
358  *
359  * @exception GException::out_of_range
360  * Sky region index is out of range.
361  *
362  * Remove sky region of specified @p index from container.
363  ***************************************************************************/
364 void GSkyRegions::remove(const int& index)
365 {
366  // Compile option: raise exception if index is out of range
367  #if defined(G_RANGE_CHECK)
368  if (index < 0 || index >= size()) {
369  throw GException::out_of_range(G_REMOVE1, "Sky Region index",
370  index, size());
371  }
372  #endif
373 
374  // Delete region
375  delete m_regions[index];
376 
377  // Erase region component from container
378  m_regions.erase(m_regions.begin() + index);
379 
380  // Return
381  return;
382 }
383 
384 
385 /***********************************************************************//**
386  * @brief Append region container
387  *
388  * @param[in] regions region container.
389  *
390  * Append region container to the container.
391  ***************************************************************************/
392 void GSkyRegions::extend(const GSkyRegions& regions)
393 {
394  // Do nothing if region container is empty
395  if (!regions.is_empty()) {
396 
397  // Get size. Note that we extract the size first to avoid an
398  // endless loop that arises when a container is appended to
399  // itself.
400  int num = regions.size();
401 
402  // Reserve enough space
403  reserve(size() + num);
404 
405  // Loop over all region components and append pointers to deep copies
406  for (int i = 0; i < num; ++i) {
407 
408  // Append region to container
409  m_regions.push_back(regions[i]->clone());
410 
411  } // endfor: looped over all regions
412 
413  } // endif: region container was not empty
414 
415  // Return
416  return;
417 }
418 
419 
420 /***********************************************************************//**
421  * @brief Check if direction is contained in one of the regions
422  *
423  * @param[in] dir Sky direction.
424  * @return True if the sky direction is container in one of the regions,
425  * falls otherwise.
426  *
427  * Checks if a sky direction is contained in one of the regions of the
428  * container.
429  ***************************************************************************/
430 bool GSkyRegions::contains(const GSkyDir& dir) const
431 {
432  // Initialise return value
433  bool overlap = false;
434 
435  // Loop over regions
436  for (int i = 0; i < size(); ++i) {
437  overlap = m_regions[i]->contains(dir);
438  if (overlap) {
439  break;
440  }
441  }
442 
443  // Return result
444  return overlap;
445 }
446 
447 
448 /***********************************************************************//**
449  * @brief Check if region overlaps one of the regions
450  *
451  * @param[in] region Sky region.
452  * @return True if @p region overlaps with one of the regions in the region
453  * container, false otherwise
454  *
455  * Tells if region overlaps one of the regions
456  ***************************************************************************/
457 bool GSkyRegions::overlaps(const GSkyRegion& region) const
458 {
459  // Initialise return value
460  bool overlap = false;
461 
462  // Loop over regions
463  for (int i = 0; i < size(); ++i) {
464  overlap = m_regions[i]->overlaps(region);
465  if (overlap) {
466  break;
467  }
468  }
469 
470  // Return result
471  return overlap;
472 }
473 
474 
475 /***********************************************************************//**
476  * @brief Check if any of the regions in two containers overlap
477  *
478  * @param[in] regions Sky region container.
479  * @return True if one of the regions in the @p regions container overlaps
480  * with one of the regions in the container, false otherwise
481  *
482  * Tells if all regions in overlaps one of the regions. Note, this method
483  * returns true if ANY of the regions in the two containers overlap with
484  * each other
485  ***************************************************************************/
486 bool GSkyRegions::overlaps(const GSkyRegions& regions) const
487 {
488  // Initialize return value
489  bool overlap = false;
490 
491  // Loop over each region in the container
492  for (int i = 0; i < size(); ++i) {
493  overlap = regions.overlaps(*m_regions[i]);
494  if (overlap) {
495  break;
496  }
497  }
498 
499  // Return result
500  return overlap;
501 }
502 
503 
504 /***********************************************************************//**
505  * @brief Load regions from DS9 region file
506  *
507  * @param[in] filename DS9 region filename.
508  *
509  * @exception GException::file_error
510  * Unable to open file.
511  *
512  * Loads all regions from a DS9 region file.
513  ***************************************************************************/
514 void GSkyRegions::load(const GFilename& filename)
515 {
516  // Clear any existing regions
517  clear();
518 
519  // Open file. Throw an exception if opening failed.
520  std::ifstream ds9file;
521  ds9file.open(filename.url().c_str());
522  if (ds9file.is_open()) {
523 
524  // Loop over file lines
525  std::string fileline = "";
526  std::string coordsys = "galactic";
527  while (ds9file.good()) {
528 
529  // Read one line
530  getline(ds9file,fileline);
531 
532  // If line is a comment then continue
533  if (fileline[0] == '#') {
534  continue;
535  }
536 
537  // Check for global definition of coordinate system
538  if (std::string::npos != fileline.find("fk5")) {
539  coordsys = "fk5";
540  }
541  else if (std::string::npos != fileline.find("icrs")) {
542  coordsys = "icrs";
543  }
544 
545  // If region is a circle then read circle
546  if (std::string::npos != fileline.find("circle")) {
547 
548  // Create instance of GSkyRegionCircle object
549  GSkyRegionCircle region;
550 
551  // If coordinate system and region defined on the same line
552  if ((std::string::npos != fileline.find("fk5")) ||
553  (std::string::npos != fileline.find("icrs")) ||
554  (std::string::npos != fileline.find("galactic"))) {
555  region.read(fileline);
556  append(region);
557  }
558 
559  // else, prepend the coordinate system
560  else {
561  std::string newfileline = coordsys;
562  newfileline.append("; ");
563  newfileline.append(fileline);
564  region.read(newfileline);
565  append(region);
566  }
567 
568  } // endif: region was circle
569 
570  // ... otherwise if region is a box then read rectangle
571  else if (std::string::npos != fileline.find("box")) {
572 
573  // Create instance of GSkyRegionRectangle object
574  GSkyRegionRectangle region;
575 
576  // If coordinate system and region defined on the same line
577  if ((std::string::npos != fileline.find("fk5")) ||
578  (std::string::npos != fileline.find("icrs")) ||
579  (std::string::npos != fileline.find("galactic"))) {
580  region.read(fileline);
581  append(region);
582  }
583 
584  // else, prepend the coordinate system
585  else {
586  std::string newfileline = coordsys;
587  newfileline.append("; ");
588  newfileline.append(fileline);
589  region.read(newfileline);
590  append(region);
591  }
592 
593  } // endif: region was box
594 
595  } // endwhile: looped over file
596 
597  // Close file
598  ds9file.close();
599 
600  // Store filename
602 
603  }
604 
605  // File could not be opened
606  else {
607  std::string msg = "Unable to open file \""+filename.url()+"\". Please "
608  "specify a valid file name.";
609  throw GException::file_error(G_LOAD, msg);
610  }
611 
612  // Return
613  return;
614 }
615 
616 
617 /***********************************************************************//**
618  * @brief Save regions into DS9 region file
619  *
620  * @param[in] filename DS9 region filename.
621  *
622  * @exception GException::file_error
623  * Unable to open file.
624  *
625  * Saves all regions in the container into a DS9 region file.
626  ***************************************************************************/
627 void GSkyRegions::save(const GFilename& filename) const
628 {
629  // Open file
630  std::ofstream ds9file;
631  ds9file.open(filename.url().c_str());
632 
633  // If file opened correctly, then save regions
634  if (ds9file.is_open()) {
635 
636  // Write global definition
637  std::string fileline;
638  fileline.append("# Region file format: DS9 version 4.1\n");
639  fileline.append("global color=green dashlist=8 3 width=1");
640  ds9file << fileline << "\n";
641 
642  // Loop over regions in container
643  for (int i = 0; i < size(); ++i) {
644  ds9file << m_regions[i]->write() << "\n";
645  }
646 
647  // Close file
648  ds9file.close();
649 
650  // Store filename
652 
653  }
654 
655  // ... otherwise, if file could not be opened then throw an exception
656  else {
657  std::string msg = "Unable to open file \""+filename.url()+"\". Please "
658  "specify a valid file name.";
659  throw GException::file_error(G_SAVE, msg);
660  }
661 
662  // Return
663  return;
664 }
665 
666 
667 /***********************************************************************//**
668  * @brief Print regions
669  *
670  * @param[in] chatter Chattiness.
671  * @return String containing region container information.
672  *
673  * Prints all regions into a string.
674  ***************************************************************************/
675 std::string GSkyRegions::print(const GChatter& chatter) const
676 {
677  // Initialise result string
678  std::string result;
679 
680  // Continue only if chatter is not silent
681  if (chatter != SILENT) {
682 
683  // Append header
684  result.append("=== GSkyRegions ===");
685 
686  // Append information
687  result.append("\n"+gammalib::parformat("Number of regions"));
688  result.append(gammalib::str(size()));
689 
690  // Append regions
691  for (int i = 0; i < size(); ++i) {
692  result.append("\n"+m_regions[i]->print(chatter));
693  }
694 
695  } // endif: chatter was not silent
696 
697  // Return result
698  return result;
699 }
700 
701 
702 /*==========================================================================
703  = =
704  = Private methods =
705  = =
706  ==========================================================================*/
707 
708 /***********************************************************************//**
709  * @brief Initialise class members
710  ***************************************************************************/
712 {
713  // Initialise members
714  m_filename.clear();
715  m_regions.clear();
716 
717  // Return
718  return;
719 }
720 
721 
722 /***********************************************************************//**
723  * @brief Copy class members
724  *
725  * @param[in] regions region container.
726  *
727  * Makes a copy of all class members. All regions are deep copied.
728  ***************************************************************************/
730 {
731  // Copy members
732  m_filename = regions.m_filename;
733 
734  // Copy regions
735  for (int i = 0; i < regions.m_regions.size(); ++i) {
736  m_regions.push_back((regions.m_regions[i]->clone()));
737  }
738 
739  // Return
740  return;
741 }
742 
743 
744 /***********************************************************************//**
745  * @brief Delete class members
746  *
747  * Deallocates all regions. The method loops over the region container and
748  * deallocates the memory that has been allocated before.
749  ***************************************************************************/
751 {
752  // Free regions
753  for (int i = 0; i < m_regions.size(); ++i) {
754  if (m_regions[i] != NULL) {
755  delete m_regions[i];
756  }
757  m_regions[i] = NULL;
758  }
759 
760  // Return
761  return;
762 }
virtual GSkyRegion * clone(void) const =0
Clones object.
void clear(void)
Clear object.
Rectangular sky region class interface definition.
#define G_AT
Definition: GSkyRegions.cpp:42
#define G_SAVE
Definition: GSkyRegions.cpp:52
std::vector< GSkyRegion * > m_regions
List of regions.
Sky direction class interface definition.
GSkyRegion * insert(const int &index, const GSkyRegion &region)
Insert region into container.
Sky regions container class definition.
void copy_members(const GSkyRegions &regions)
Copy class members.
Definition of interface for all GammaLib classes.
GSkyRegions * clone(void) const
Clone instance.
Gammalib tools definition.
void read(const std::string &line)
Read region from DS9 string.
GSkyRegions & operator=(const GSkyRegions &regions)
Assignment operator.
Interface for the circular sky region class.
Interface for the rectangular sky region class.
GSkyRegions(void)
Void constructor.
Definition: GSkyRegions.cpp:70
#define G_SET1
Definition: GSkyRegions.cpp:43
std::string print(const GChatter &chatter=NORMAL) const
Print regions.
const GFilename & filename(void) const
Return regions file name.
Abstract interface for the sky region class.
Definition: GSkyRegion.hpp:57
GFilename m_filename
Filename of origin.
#define G_INSERT1
Definition: GSkyRegions.cpp:46
Filename class.
Definition: GFilename.hpp:62
void save(const GFilename &filename) const
Save regions into DS9 region file.
GChatter
Definition: GTypemaps.hpp:33
int size(void) const
Return number of regions in container.
GSkyRegion * at(const int &index)
Return pointer to region.
#define G_REMOVE1
Definition: GSkyRegions.cpp:48
virtual ~GSkyRegions(void)
Destructor.
Sky region container class.
Definition: GSkyRegions.hpp:56
bool is_empty(void) const
Signals if there are no regions in container.
#define G_LOAD
Definition: GSkyRegions.cpp:51
std::string url(void) const
Return Uniform Resource Locator (URL)
Definition: GFilename.hpp:189
void clear(void)
Clear file name.
Definition: GFilename.cpp:188
bool overlaps(const GSkyRegion &region) const
Check if region overlaps one of the regions.
void free_members(void)
Delete class members.
void extend(const GSkyRegions &regions)
Append region container.
void init_members(void)
Initialise class members.
GSkyRegion * set(const int &index, const GSkyRegion &region)
Set region in container.
void remove(const int &index)
Remove region from container.
std::string parformat(const std::string &s, const int &indent=0)
Convert string in parameter format.
Definition: GTools.cpp:1143
Sky direction class.
Definition: GSkyDir.hpp:62
bool contains(const GSkyDir &dir) const
Check if direction is contained in one of the regions.
void read(const std::string &line)
Read region from DS9 string.
Circular sky region class interface definition.
Abstract sky region base class interface definition.
void reserve(const int &num)
Reserves space for regions in container.
void load(const GFilename &filename)
Load regions from DS9 region file.
std::string str(const unsigned short int &value)
Convert unsigned short integer value into string.
Definition: GTools.cpp:489
GSkyRegion * append(const GSkyRegion &region)
Append region to container.