do not attempt get the person object when no one is logged in...
[sgn.git] / lib / CXGN / NOAANCDC.pm
blobb5999cb07c8a5eca64ac7481839f545bdb59bf1c
2 =head1 NAME
4 CXGN::NOAANCDC - helper class for getting weather data from NOAA NCDC weather stations
6 =head1 SYNOPSYS
8 my $noaa = CXGN::NOAANCDC->new({
9 bcs_schema => $schema,
10 data_types => ['TMIN', 'TMAX', 'PRCP'],
11 start_date => "2019-01-13", #YYYY-MM-DD
12 end_date => "2019-12-19", #YYYY-MM-DD
13 noaa_station_id => "GHCND:US1NCBC0005",
14 noaa_ncdc_access_token => $noaa_ncdc_access_token
15 });
16 my $temperature_averaged_growing_degree_days = $noaa->get_temperature_averaged_gdd($gdd_base_temperature);
18 # STILL HAS BUGS WITH MULTIYEAR QUERIES STARTING/ENDING WITH YYYY/01/01 or YYYY/12/31 DUE TO do_query_max_one_year()...
20 # Musgrave stationid = GHCND:USC00300331
22 # Datatypes:
23 # PRCP Total Precipitation (+Musgrave)
24 # F2MN Faster 2 minute wind speed (-Musgrave)
25 # TMAX Maximum Temperature (+Musgrave)
26 # TMIN Minimum Temperature (+Musgrave)
27 # TOBS Temperatue at observation time (+Musgrave)
28 # TAVG Average Temperature (-Musgrave)
29 # F5SC Fastest 5 second wind speed (-Musgrave)
30 # AWND Average Wind Speed (-Musgrave)
31 # FSMI Fastest Mile (ddfff) (-Musgrave)
32 # FSMN Fastest One-minute Wind (ddfff) (-Musgrave)
33 # PRES Station Pressure (-Musgrave)
34 # RWND Resultant Wind Speed (-Musgrave)
35 # SLVP Sea Level Pressure (-Musgrave)
36 # TMPW Wet Bulb Temperature (-Musgrave)
37 # FSIN Fastest Instantaneous Wind (ddfff) (-Musgrave)
38 # WDMV 24_hour Wind Movement (-Musgrave)
39 # MNTP Average Temperature (-Musgrave)
40 # DPTP Dew Point Temperature (-Musgrave)
42 =head1 AUTHOR
44 Nicolas Morales <nm529@cornell.edu>
46 =head1 METHODS
48 =cut
50 package CXGN::NOAANCDC;
52 use Moose;
53 use Data::Dumper;
54 use Try::Tiny;
55 use SGN::Model::Cvterm;
56 use LWP::UserAgent;
57 use JSON;
58 use Math::Round;
59 use Time::Piece;
60 use Time::Seconds;
62 has 'bcs_schema' => (
63 isa => 'Bio::Chado::Schema',
64 is => 'rw',
65 required => 1
68 has 'data_types' => (
69 isa => 'ArrayRef',
70 is => 'rw',
73 has 'start_date' => (
74 isa => 'Str',
75 is => 'rw',
76 required => 1
79 has 'end_date' => (
80 isa => 'Str',
81 is => 'rw',
82 required => 1
85 has 'noaa_station_id' => (
86 isa => 'Str',
87 is => 'rw',
88 required => 1
91 has 'noaa_ncdc_access_token' => (
92 isa => 'Str',
93 is => 'rw',
94 required => 1
97 sub get_noaa_data {
98 my $self = shift;
99 my $start_date = $self->start_date;
100 my $end_date = $self->end_date;
102 my $data_types_string = '&datatypeid=';
103 $data_types_string .= join '&datatypeid=', @{$self->data_types};
105 my $ua = LWP::UserAgent->new(
106 ssl_opts => { verify_hostname => 0 }
108 my $server_endpoint = "https://www.ncdc.noaa.gov/cdo-web/api/v2/data?stationid=".$self->noaa_station_id."&limit=1000&datasetid=GHCND".$data_types_string."&startdate=".$start_date."&enddate=".$end_date;
110 print STDERR $server_endpoint."\n";
111 my $resp = $ua->get($server_endpoint, "token"=>$self->noaa_ncdc_access_token);
113 my $message_hash = {};
114 if ($resp->is_success) {
115 my $message = $resp->decoded_content;
116 $message_hash = decode_json $message;
117 # print STDERR Dumper $message_hash;
119 else {
120 my $message = $resp->decoded_content;
121 print STDERR Dumper $message;
122 $message_hash = decode_json $message;
124 return $message_hash;
127 sub do_query_max_one_year {
128 my $self = shift;
129 my $start_date = $self->start_date;
130 my $end_date = $self->end_date;
132 my $start_date_object = Time::Piece->strptime($start_date, "%Y-%m-%d");
133 my $end_date_object = Time::Piece->strptime($end_date, "%Y-%m-%d");
134 my $time_diff = $end_date_object - $start_date_object;
135 my $time_diff_days = $time_diff->days;
136 print STDERR Dumper $time_diff_days;
137 my $end_date_epoch = $end_date_object->epoch;
139 my @time_ranges;
140 if ($time_diff_days > 365) {
141 my $day_start = Time::Piece->strptime($start_date, "%Y-%m-%d");
142 my $year_counter = 0;
143 my $day_end_year = $day_start + ONE_DAY * ( 365 - $day_start->day_of_year );
144 my $day_end_year_start_original = $day_end_year;
145 while (1) {
146 if ($year_counter != 0) {
147 $day_end_year = $day_start + ONE_DAY * ( 365 - $day_start->day_of_year );
150 if ( $day_end_year > $end_date_object ) {
151 push @time_ranges, [$day_start->strftime("%Y-%m-%d"), $end_date_object->strftime("%Y-%m-%d")];
152 last;
154 else {
155 if ($year_counter == 0) {
156 push @time_ranges, [$day_start->strftime("%Y-%m-%d"), $day_end_year->strftime("%Y-%m-%d")];
157 $day_start = $day_end_year_start_original + ONE_YEAR * $year_counter;
159 else {
160 push @time_ranges, [$day_start->strftime("%Y-01-01"), $day_start->strftime("%Y-12-31")];
161 $day_start = $day_end_year_start_original + ONE_YEAR * $year_counter + 1 * ONE_DAY;
163 $day_start = $day_start->truncate(to => 'day');
165 $year_counter++;
168 else {
169 push @time_ranges, [$start_date, $end_date];
171 print STDERR Dumper \@time_ranges;
173 my %weather_hash;
174 foreach (@time_ranges) {
175 sleep(1);
176 $self->start_date($_->[0]);
177 $self->end_date($_->[1]);
178 my $message_hash = $self->get_noaa_data();
179 # print STDERR Dumper $message_hash;
180 if ($message_hash->{status} eq '400') {
181 return {error => $message_hash->{message}};
184 foreach (@{$message_hash->{results}}) {
185 $weather_hash{$_->{date}}->{$_->{datatype}} = $_->{value};
188 return \%weather_hash;
191 sub get_temperature_averaged_gdd {
192 my $self = shift;
193 my $gdd_base_temperature = shift || '50'; #For Maize use 50
194 my $result = 0;
196 $self->data_types(['TMIN','TMAX']);
198 my $weather_hash = $self->do_query_max_one_year();
200 foreach (values %$weather_hash) {
201 if (defined($_->{TMIN}) && defined($_->{TMAX})) {
202 #TMAX and TMIN are in tenths of C
203 my $tmax_f = (9/5)*($_->{TMAX}/10) + 32;
204 my $tmin_f = (9/5)*($_->{TMIN}/10) + 32;
205 my $gdd_accumulation = (($tmax_f + $tmin_f)/2) - $gdd_base_temperature;
206 if ($gdd_accumulation > 0) {
207 $result = $result + $gdd_accumulation;
212 return {gdd => round($result) };
215 sub get_averaged_precipitation {
216 my $self = shift;
217 my $result = 0;
219 $self->data_types(['PRCP']);
221 my $weather_hash = $self->do_query_max_one_year();
223 foreach (values %$weather_hash) {
224 if (defined($_->{PRCP})) {
225 $result = $result + $_->{PRCP};
229 return $result;
232 sub get_daily_values {
233 my $self = shift;
234 my $result;
236 my $weather_hash = $self->do_query_max_one_year();
238 while (my ($date, $do) = each %$weather_hash) {
239 while (my ($data_type, $value) = each %$do) {
240 $result->{$date}->{$data_type} = $value;
243 my @sorted_dates = sort keys %$result;
245 return ($result, \@sorted_dates);