4 CXGN::Location - helper class for locations
8 my $location = CXGN::Location->new( { bcs_schema => $schema } );
9 $location->set_altitude(280);
14 Bryan Ellerbrock <bje24@cornell.edu>
20 package CXGN
::Location
;
25 use SGN
::Model
::Cvterm
;
26 use CXGN
::BrAPI
::v2
::ExternalReferences
;
29 isa
=> 'Bio::Chado::Schema',
35 isa
=> 'Bio::Chado::Schema::Result::NaturalDiversity::NdGeolocation',
39 has
'nd_geolocation_id' => (
49 has
'abbreviation' => (
54 has
'country_name' => (
59 has
'country_code' => (
64 has
'breeding_programs' => (
69 has
'location_type' => (
89 has
'noaa_station_id' => (
94 has
'external_references' => (
95 isa
=> 'Maybe[ArrayRef[HashRef[Str]]]',
102 print STDERR
"RUNNING BUILD FOR LOCATION.PM...\n";
104 if ($self->nd_geolocation_id){
105 $location = $self->bcs_schema->resultset("NaturalDiversity::NdGeolocation")->find( { nd_geolocation_id
=> $self->nd_geolocation_id });
106 $self->location($location);
108 if (defined $location) {
109 $self->location( $self->location || $location );
110 $self->nd_geolocation_id( $self->nd_geolocation_id || $location->nd_geolocation_id );
111 $self->name( $self->name || $location->description );
112 $self->abbreviation( $self->abbreviation || $self->_get_ndgeolocationprop('abbreviation', 'geolocation_property'));
113 $self->country_name( $self->country_name || $self->_get_ndgeolocationprop('country_name', 'geolocation_property'));
114 $self->country_code( $self->country_code || $self->_get_ndgeolocationprop('country_code', 'geolocation_property'));
115 $self->breeding_programs( $self->breeding_programs || $self->_get_ndgeolocationprop('breeding_program', 'project_property'));
116 $self->location_type( $self->location_type || $self->_get_ndgeolocationprop('location_type', 'geolocation_property'));
117 $self->latitude( $self->latitude || $location->latitude);
118 $self->longitude( $self->longitude || $location->longitude);
119 $self->altitude( $self->altitude || $location->altitude);
120 $self->noaa_station_id( $self->noaa_station_id || $self->_get_ndgeolocationprop('noaa_station_id', 'geolocation_property'));
121 $self->external_references($self->external_references);
124 print STDERR
"Breeding programs are: ".$self->breeding_programs()."\n";
131 my $schema = $self->bcs_schema();
134 my $nd_geolocation_id = $self->nd_geolocation_id();
135 my $name = _trim
($self->name());
136 my $abbreviation = $self->abbreviation();
137 my $country_name = $self->country_name();
138 my $country_code = $self->country_code();
139 my $breeding_programs = $self->breeding_programs();
140 my $location_type = $self->location_type();
141 my $latitude = $self->latitude();
142 my $longitude = $self->longitude();
143 my $altitude = $self->altitude();
144 my $noaa_station_id = $self->noaa_station_id();
145 my $external_references = $self->external_references();
147 # Validate properties
149 if (!$nd_geolocation_id && !$name) {
150 return { error
=> "Cannot add a new location with an undefined name. A location name is required" };
152 elsif (!$nd_geolocation_id && !$self->_is_valid_name($name)) { # can't add a new location with name that already exists
153 return { error
=> "The location - $name - already exists. Please choose another name, or use the existing location" };
156 if (!$nd_geolocation_id && $abbreviation && !$self->_is_valid_abbreviation($abbreviation)) {
157 return { error
=> "Abbreviation $abbreviation already exists in the database. Please choose another abbreviation" };
160 if ($country_name && $country_name =~ m/[0-9]/) {
161 return { error
=> "Country name $country_name is not a valid ISO standard country name." };
164 if ($country_code && (($country_code !~ m/^[^a-z]*$/) || (length($country_code) != 3 ))) {
165 return { error
=> "Country code $country_code is not a valid ISO Alpha-3 code." };
168 my @breeding_program_ids;
169 foreach my $breeding_program (split ("&", $breeding_programs)) {
170 $breeding_program = _trim
($breeding_program);
171 if ($breeding_program && !$self->_is_valid_program($breeding_program)) { # can't use a breeding program that doesn't exist
172 return { error
=> "Breeding program $breeding_program doesn't exist in the database." };
174 push @breeding_program_ids, $self->bcs_schema->resultset("Project::Project")->search({ name
=> $breeding_program })->first->project_id();
177 my $breeding_program_ids = join '&', @breeding_program_ids;
179 if ($location_type && !$self->_is_valid_type($location_type)) {
180 return { error
=> "Location type $location_type must be must be one of the following: Town, Farm, Field, Greenhouse, Screenhouse, Lab, Storage, Other." };
183 if ( ($latitude && $latitude !~ /^-?[0-9.]+$/) || ($latitude && $latitude < -90) || ($latitude && $latitude > 90)) {
184 return { error
=> "Latitude (in degrees) must be a number between 90 and -90." };
187 if ( ($longitude && $longitude !~ /^-?[0-9.]+$/) || ($longitude && $longitude < -180) || ($longitude && $longitude > 180)) {
188 return { error
=> "Longitude (in degrees) must be a number between 180 and -180." };
191 if ( ($altitude && $altitude !~ /^-?[0-9.]+$/) || ($altitude && $altitude < -418) || ($altitude && $altitude > 8848) ) {
192 return { error
=> "Altitude (in meters) must be a number between -418 (Dead Sea) and 8,848 (Mt. Everest)." };
195 # Add new location if no id supplied
196 if (!$nd_geolocation_id) {
197 print STDERR
"Checks completed, adding new location $name\n";
199 my $new_row = $schema->resultset('NaturalDiversity::NdGeolocation')
201 description
=> $name,
204 if (length $longitude) { $new_row->longitude($longitude); }
205 if (length $latitude) { $new_row->latitude($latitude); }
206 if (length $altitude) { $new_row->altitude($altitude); }
209 #$self->ndgeolocation_id($new_row->ndgeolocation_id());
210 $self->location($new_row);
213 $self->_store_ndgeolocationprop('abbreviation', 'geolocation_property', $abbreviation);
216 $self->_store_ndgeolocationprop('country_name', 'geolocation_property', $country_name);
219 $self->_store_ndgeolocationprop('country_code', 'geolocation_property', $country_code);
221 if ($breeding_programs){
222 $self->_store_breeding_programs($breeding_program_ids);
225 $self->_store_ndgeolocationprop('location_type', 'geolocation_property', $location_type);
227 if ($noaa_station_id){
228 $self->_store_ndgeolocationprop('noaa_station_id', 'geolocation_property', $noaa_station_id);
231 # save external references if specified
232 if ($external_references) {
233 my $references = CXGN
::BrAPI
::v2
::ExternalReferences
->new({
234 bcs_schema
=> $schema,
235 external_references
=> $external_references,
236 table_name
=> 'nd_geolocation',
237 table_id_key
=> 'nd_geolocation_id',
238 id
=> $self->location()->nd_geolocation_id()
241 $references->store();
243 if ($references->{'error'}) {
244 return { error
=> $references->{'error'} };
249 my $transaction_error;
252 $schema->txn_do($coderef);
254 $transaction_error = $_;
257 if ($transaction_error) {
258 print STDERR
"Error creating location $name: $transaction_error\n";
259 return { error
=> $transaction_error };
261 print STDERR
"Location $name added successfully\n";
262 return { success
=> "Location $name added successfully\n", nd_geolocation_id
=>$self->location()->nd_geolocation_id() };
265 # Edit existing location if id supplied
266 elsif ($nd_geolocation_id) {
267 print STDERR
"Checks completed, editing existing location $name\n";
269 my $row = $schema->resultset("NaturalDiversity::NdGeolocation")->find({ nd_geolocation_id
=> $nd_geolocation_id });
270 $row->description($name);
271 $row->latitude($latitude);
272 $row->longitude($longitude);
273 $row->altitude($altitude);
275 $self->_update_ndgeolocationprop('abbreviation', 'geolocation_property', $abbreviation);
276 $self->_update_ndgeolocationprop('country_name', 'geolocation_property', $country_name);
277 $self->_update_ndgeolocationprop('country_code', 'geolocation_property', $country_code);
278 $self->_update_ndgeolocationprop('location_type', 'geolocation_property', $location_type);
279 $self->_update_ndgeolocationprop('noaa_station_id', 'geolocation_property', $noaa_station_id);
280 $self->_store_breeding_programs($breeding_program_ids);
287 print STDERR
"Error editing location $name: $error\n";
288 return { error
=> $error };
290 print STDERR
"Location $name was successfully updated\n";
291 return { success
=> "Location $name was successfully updated\n", nd_geolocation_id
=>$self->location()->nd_geolocation_id() };
296 sub delete_location
{
298 my $row = $self->bcs_schema->resultset("NaturalDiversity::NdGeolocation")->find({ nd_geolocation_id
=> $self->nd_geolocation_id() });
299 my $name = $row->description();
300 my @experiments = $row->nd_experiments;
301 #print STDERR "Associated experiments: ".Dumper(@experiments)."\n";
304 my $error = "Location $name cannot be deleted because there are ".scalar @experiments." measurements associated with it from at least one trial.\n";
306 return { error
=> $error };
310 my $location_type_id = SGN
::Model
::Cvterm
->get_cvterm_row($self->bcs_schema, 'project location', 'project_property')->cvterm_id();
311 my $projectprop_rows = $self->bcs_schema->resultset("Project::Projectprop")->search({ value
=> $self->nd_geolocation_id(), type_id
=> $location_type_id });
312 while (my $r = $projectprop_rows->next()){ # remove any links to deleted location in projectprop
315 return { success
=> "Location $name was successfully deleted.\n" };
319 sub _get_ndgeolocationprop
{
324 my $ndgeolocationprop_type_id = SGN
::Model
::Cvterm
->get_cvterm_row($self->bcs_schema, $type, $cv)->cvterm_id();
325 my $rs = $self->bcs_schema()->resultset("NaturalDiversity::NdGeolocationprop")->search({ nd_geolocation_id
=> $self->nd_geolocation_id(), type_id
=> $ndgeolocationprop_type_id }, { order_by
=> {-asc
=> 'nd_geolocationprop_id'} });
328 while (my $r = $rs->next()){
329 push @results, $r->value;
331 my $res = join '&', @results;
335 sub _update_ndgeolocationprop
{
340 my $existing_prop = $self->_get_ndgeolocationprop($type, $cv);
343 $self->_store_ndgeolocationprop($type, $cv, $value);
344 } elsif ($existing_prop) {
345 $self->_remove_ndgeolocationprop($type, $cv, $existing_prop);
349 sub _store_breeding_programs
{
351 my $new_programs = shift;
352 my @new_programs = split ("&", $new_programs);
353 my $existing_programs = $self->_get_ndgeolocationprop('breeding_program', 'project_property');
354 my @existing_programs = split ("&", $existing_programs);
356 foreach my $existing_program (@existing_programs) {
357 # print STDERR "Removing existing program $existing_program\n";
358 $existing_program = _trim
($existing_program);
359 $self->_remove_ndgeolocationprop('breeding_program', 'project_property', $existing_program)
361 foreach my $new_program (@new_programs) {
362 # print STDERR "Storing new program $new_program\n";
363 $new_program = _trim
($new_program);
364 $self->location->create_geolocationprops({ 'breeding_program' => $new_program}, {cv_name
=> 'project_property' });
368 sub _store_ndgeolocationprop
{
373 #print STDERR " Storing value $value with type $type\n";
374 my $type_id = SGN
::Model
::Cvterm
->get_cvterm_row($self->bcs_schema, $type, $cv)->cvterm_id();
375 my $row = $self->bcs_schema()->resultset("NaturalDiversity::NdGeolocationprop")->find( { type_id
=>$type_id, nd_geolocation_id
=> $self->nd_geolocation_id() } );
381 my $stored_ndgeolocationprop = $self->location->create_geolocationprops({ $type => $value}, {cv_name
=> $cv });
385 sub _remove_ndgeolocationprop
{
390 my $type_id = SGN
::Model
::Cvterm
->get_cvterm_row($self->bcs_schema, $type, $cv)->cvterm_id();
391 my $rs = $self->bcs_schema()->resultset("NaturalDiversity::NdGeolocationprop")->search( { type_id
=>$type_id, nd_geolocation_id
=> $self->nd_geolocation_id(), value
=>$value } );
393 if ($rs->count() == 1) {
394 $rs->first->delete();
397 elsif ($rs->count() == 0) {
401 print STDERR
"Error removing ndgeolocationprop from location ".$self->ndgeolocation_id().". Please check this manually.\n";
410 my $schema = $self->bcs_schema();
411 my $existing_name_count = $schema->resultset('NaturalDiversity::NdGeolocation')->search( { description
=> $name } )->count();
412 if ($existing_name_count > 0) {
420 sub _is_valid_abbreviation
{
422 my $abbreviation = shift;
423 my $schema = $self->bcs_schema();
424 my $existing_abbreviation_count = $schema->resultset('NaturalDiversity::NdGeolocationprop')->search( { value
=> $abbreviation } )->count();
425 if ($existing_abbreviation_count > 0) {
433 sub _is_valid_program
{
436 my $schema = $self->bcs_schema();
437 my $existing_program_count = $schema->resultset('Project::Project')->search(
439 'type.name'=> 'breeding_program',
440 'me.name' => $program
449 if ($existing_program_count < 1) {
470 if (!$valid_types{$type}) {
478 sub _trim
{ #trim whitespace from both ends of a string
480 $s =~ s/^\s+|\s+$//g;