File Coverage

blib/lib/Astro/DSS/JPEG.pm
Criterion Covered Total %
statement 71 71 100.0
branch 26 26 100.0
condition 27 27 100.0
subroutine 13 13 100.0
pod 2 2 100.0
total 139 139 100.0


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