4 CXGN::NOAANCDC - helper class for getting weather data from NOAA NCDC weather stations
8 my $noaa = CXGN::NOAANCDC->new({
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
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
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)
44 Nicolas Morales <nm529@cornell.edu>
50 package CXGN
::NOAANCDC
;
55 use SGN
::Model
::Cvterm
;
63 isa
=> 'Bio::Chado::Schema',
85 has
'noaa_station_id' => (
91 has
'noaa_ncdc_access_token' => (
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;
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
{
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;
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;
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")];
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;
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');
169 push @time_ranges, [$start_date, $end_date];
171 print STDERR Dumper \
@time_ranges;
174 foreach (@time_ranges) {
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
{
193 my $gdd_base_temperature = shift || '50'; #For Maize use 50
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
{
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
};
232 sub get_daily_values
{
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);