File Coverage

blib/lib/Astro/DSS/JPEG.pm
Criterion Covered Total %
statement 69 69 100.0
branch 28 28 100.0
condition 27 27 100.0
subroutine 12 12 100.0
pod 2 2 100.0
total 138 138 100.0


line stmt bran cond sub pod time code
1             package Astro::DSS::JPEG;
2              
3 2     2   358310 use 5.006;
  2         13  
4 2     2   11 use strict;
  2         4  
  2         39  
5 2     2   9 use warnings;
  2         13  
  2         66  
6 2     2   628 use utf8;
  2         17  
  2         13  
7              
8 2     2   1444 use LWP::UserAgent;
  2         92805  
  2         77  
9 2     2   17 use List::Util 'min';
  2         4  
  2         2348  
10              
11             =encoding utf8
12              
13             =head1 NAME
14              
15             Astro::DSS::JPEG - Download color JPEG images from the Digitized Sky Survey
16              
17             =head1 VERSION
18              
19             Version 0.01_1
20              
21             =cut
22              
23             our $VERSION = '0.01_1';
24              
25              
26             =head1 SYNOPSIS
27              
28             use Astro::DSS::JPEG;
29              
30             my $dss = Astro::DSS::JPEG->new();
31              
32             #get an object (Triangulum galaxy M33) by name at default 1000x1000,
33             #getting coordinates and frame size automatically from SIMBAD
34             my $image = $dss->get_image(target => 'M33');
35              
36             #Save an image directly to file manually specifying coordinates, frame of
37             #90x90 arcmin, 2048x2048 pixels
38             $dss->get_image(
39             ra => '01 33 50.904',
40             dec => '+30 39 35.79',
41             angular_size => 90,
42             pixel_size => 2048,
43             filename => $filename
44             );
45              
46             #In one go, save Andromeda Galaxy to andromeda.jpg
47             Astro::DSS::JPEG->new->get_image(target=>'Andromeda Galaxy', filename=>'andromeda.jpg');
48              
49             =head1 DESCRIPTION
50              
51             Astro::DSS::JPEG downloads JPEG images for any location in the sky from the L.
52             It is meant to be a simple stand alone module to access a fast JPEG-only API that
53             provides color composites made from the blue and red DSS surveys.
54             In comparison, there is an old/not updated L module that would provide
55             access to the slow FITS/GIF interface of the separate DSS1/DSS2 surveys.
56              
57             Optionally, L is used if you'd like to use
58             an object name/id instead of coordinates.
59              
60             =head1 CONSTRUCTOR METHODS
61              
62             =head2 C
63              
64             my $dss = Astro::DSS::JPEG->new(
65             dss_url => 'http://gsss.stsci.edu/webservices/dssjpg/dss.svc/GetImage', # Optional
66             simbad_url => 'http://simbad.u-strasbg.fr/simbad/sim-id', # Optional
67             ua => $ua # Optional
68             );
69            
70             All parameters are optional. The constructor by default creates an L,
71             but you can pass your own with C, while the URL to the STScI JPEG endpoint
72             and the L simple id interface can be redefined
73             (e.g. if they change in the future) from the above shown defaults.
74              
75             =head1 METHODS
76              
77             =head2 C
78              
79             my $res = $dss->get_image(
80             target => $target, # Not used if RA, Dec provided
81             ra => $ra, # Right Ascension (in hours of angle)
82             dec => $dec, # Declination (in degrees of angle)
83             angular_size => 30, # Optional. Frame angular size in arcmin
84             pixel_size => 1000, # Optional. Frame size in pixels
85             filename => $filename # Optional. Filename to save image to
86             );
87              
88             Fetches an image either resolving the name through L,
89             or using RA & Dec when they are defined. If you pass a C, the JPEG image will
90             be saved to that and the function will return an L object, otherwise the
91             image data will be returned as a response.
92              
93             The parameters to pass:
94              
95             =over 4
96            
97             =item * C
98              
99             Right ascension, from 0-24h of angle. The option is flexible, it will accept a single
100             decimal number - e.g. C<2.5> or a string with hours, minutes, secs like C<'2 30 00'> or
101             C<'2h30m30s'> etc. Single/double quotes and single/double prime symbols are accepted
102             for denoting minute, second in place of a single space which also works.
103              
104             =item * C
105              
106             Declination, from -90 to 90 degrees of angle. The option is flexible, it will accept
107             a single decimal number - e.g. C<54.5> or a string with degrees, minutes, secs like
108             C<'+54 30 00'> or C<'54°30m30s'> etc. Single/double quotes and single/double prime symbols
109             are accepted for denoting minute, second in place of a single space which also works.
110              
111             =item * C
112              
113             Do a L lookup to get the C by name
114             (e.g. 'NGC598'). Will be disregarded if C and C are defined. If you have not
115             defined an C and L has one
116             defined, it will be used for the image request with an extra 50% for padding.
117              
118             =item * C
119              
120             Define the frame angular size in arcminutes, either as a single number (square) or
121             a comma separated string for a rectangle. Default is 0.5 degrees (equivalent to passing
122             C<30> or C<'30,30'> or even C<'30x30'>). The aspect ratio will be overriden by C.
123             If you care about framing, you should definitely define C, as the default
124             won't be appropriate for many targets and L often
125             does not have the info (or even has an inappropriate value). Also, large C
126             values can make the response slow (or even error out).
127              
128             =item * C
129              
130             Define the frame size in pixels, either as a single number (square) or a comma separated
131             string for a rectangle. Default is 1000 pixels (equivalent to passing C<1000> or
132             C<'1000,1000'> or even C<'1000x1000'>). Max possible is C<'4096,4096'> total size,
133             or 1 pixel / arcsecond resolution (e.g. 30*60 = 1800 pixels for 30 arcminutes), which
134             is the full resolution DSS plates were scanned at.
135              
136             =back
137              
138             =head1 NOTES
139              
140             Some artifacts can be seen at the borders of separate "stripes" of the survey and
141             also the particular JPEG endpoint used sometimes can leave the corners as plain black
142             squares (depending on the selected frame size, as it is to do with the way it does
143             segmentation), so if you want to make sure you have a frame with no corner gaps,
144             request some more angular size than you want and crop.
145              
146             Note that the module test suite won't actually fetch data from either DSS or SIMBAD.
147             This is mainly to ensure it will not fail even if the DSS & SIMBAD endpoints change,
148             as you can still use the module by passing the updated urls to the constructor. It
149             also avoids unneeded strain to those free services.
150              
151             =cut
152              
153              
154             sub new {
155 3     3 1 5903 my ($class, %opts) = @_;
156              
157 3         8 my $self = {};
158 3         6 bless($self, $class);
159              
160             $self->{dss_url} = $opts{dss_url}
161 3   100     20 || 'http://gsss.stsci.edu/webservices/dssjpg/dss.svc/GetImage';
162             $self->{simbad_url} = $opts{simbad_url}
163 3   100     13 || 'http://simbad.u-strasbg.fr/simbad/sim-id';
164             $self->{ua} = $opts{ua}
165 3   100     25 || LWP::UserAgent->new(
166             agent => "perl/Astro::DSS::JPEG/$VERSION",
167             );
168              
169 3         269 return $self;
170             }
171              
172             sub get_image {
173 5     5 1 4194 my ($self, %opts) = @_;
174 5 100 100     39 if ($opts{target} && (! defined $opts{ra} || ! defined $opts{dec})) {
      100        
175 3         12 my $simbad = $self->_get_simbad(%opts);
176 1         9 %opts = (
177             %opts,
178             %$simbad
179             );
180             }
181              
182 3         13 my $res = $self->_getDSS(_process_options(%opts));
183 3 100       582 if ($res->is_success) {
184 2 100       149 return $res if $opts{filename};
185 1         7 return $res->decoded_content;
186             } else {
187 1         12 die "*ERROR* Could not access DSS:\n".$res->status_line;
188             }
189             }
190              
191             sub _getDSS {
192 3     3   15 my ($self, %opts) = @_;
193              
194             my @request = (
195             $self->{dss_url} . sprintf(
196             "?POS=%f,%f&SIZE=%f,%f&ISIZE=%.0f,%.0f",
197             $opts{ra} * 15, # Convert to degrees
198             $opts{dec},
199             $opts{angular_size} / 60, # Convert to degrees
200             $opts{angular_size_y} / 60, # Convert to degrees
201             $opts{pixel_size},
202             $opts{pixel_size_y}
203             )
204 3         51 );
205 3 100       13 push @request, (':content_file' => $opts{filename}) if $opts{filename};
206 3         10 return $self->{ua}->get(@request);
207             }
208              
209             sub _convert_coordinates {
210 14     14   4639 my %opts = @_;
211 14 100 100     84 if (defined $opts{ra} && $opts{ra} =~ /([+-]?[0-9.]+)[ h]+([0-9.]+)[ m′']+([0-9.]+)/) {
212 4         29 $opts{ra} = $1+$2/60+$3/3600;
213             }
214 14 100 100     68 if (defined $opts{dec} && $opts{dec} =~ /([+-]?[0-9.]+)[ d°]+([0-9.]+)[ m′']+([0-9.]+)/) {
215 5 100       20 my $sign = $1 < 0 ? -1 : 1;
216 5         25 $opts{dec} = (abs($1)+$2/60+$3/3600)*$sign;
217             }
218 14         83 return %opts;
219             }
220              
221             sub _process_options {
222 8     8   4812 my %opts = _convert_coordinates(@_);
223 8   100     29 $opts{angular_size} ||= 30; # 30' Default angular size
224 8   100     22 $opts{pixel_size} ||= 1000;
225              
226 8 100       61 if ($opts{angular_size} =~ /(\S+)\s*[,xX]\s*(\S+)/) {
227 4   100     18 $opts{angular_size} = $1 || 30;
228 4         11 $opts{angular_size_y} = $2;
229             }
230 8 100       38 if ($opts{pixel_size} =~ /(\S+)\s*[,xX]\s*(\S+)/) {
231 3   100     12 $opts{pixel_size} = $1 || 1000;
232 3         7 $opts{pixel_size_y} = $2;
233             }
234              
235 8 100       23 $opts{angular_size_y} = $opts{angular_size} unless $opts{angular_size_y};
236             $opts{pixel_size_y} = $opts{pixel_size}*$opts{angular_size_y}/$opts{angular_size}
237 8 100       24 unless $opts{pixel_size_y};
238              
239             $opts{"pixel$_"} = min 4096, $opts{"pixel$_"}, $opts{"angular$_"}*60
240 8         69 for qw/_size _size_y/;
241              
242 8         64 return %opts;
243             }
244              
245             sub _get_simbad {
246 5     5   821 my ($self, %opts) = @_;
247              
248             my $res = $self->{ua}->get(
249             $self->{simbad_url}."?output.format=ASCII&Ident=".$opts{target}
250 5         26 );
251 5 100       8795 if ($res->is_success) {
252 3         215 my $simbad = $res->decoded_content;
253 3 100       174 if ($simbad =~ /\(ICRS,ep=J2000,eq=2000\): (\S+ \S+ \S+)\s+(\S+ \S+ \S+)/) {
254 2         7 $opts{ra} = $1;
255 2         5 $opts{dec} = $2;
256             } else {
257 1         9 die "*ERROR* Could not parse SIMBAD output:\n$simbad";
258             }
259 2 100       10 if ($simbad =~ /Angular size:\s*([0-9.]+)/) { # Get only largest dimension
260 1         8 $opts{angular_size} = min($1 * 1.5, 600); # Get 50% extra for framing, max out at 10 degrees
261             }
262             } else {
263 2         25 die "*ERROR* Could not access SIMBAD:\n".$res->status_line;
264             }
265              
266 2         13 return \%opts;
267             }
268              
269             =head1 AUTHOR
270              
271             Dimitrios Kechagias, C<< >>
272              
273              
274             =head1 BUGS
275              
276             Please report any bugs or feature requests to C, or through
277             the web interface at L.
278             I will be notified, and then you'll automatically be notified of progress on your bug as I make changes.
279              
280             You could also submit issues or even pull requests to the
281             github repo (see below).
282              
283              
284             =head1 GIT
285              
286             L
287              
288              
289             =head1 ACKNOWLEDGEMENTS
290              
291             The DSS images are downloaded using a public api of the L
292             provided by the L.
293              
294             Targets by name/id are resolved using the L.
295              
296              
297             =head1 LICENSE AND COPYRIGHT
298              
299             This software is copyright (c) 2020 by Dimitrios Kechagias.
300              
301             This is free software; you can redistribute it and/or modify it under
302             the same terms as the Perl 5 programming language system itself.
303              
304             For the images you download with this module, please see the L
305             for the full copyright info.
306              
307              
308             =cut
309              
310             1;