Merge pull request #42 from solgenomics/topic/duplicate_image_warning
[cxgn-corelibs.git] / lib / CXGN / Map.pm
blobeaf3023f6949ba14a04256d6510d596006c75b5b
1 package CXGN::Map;
3 =head1 NAME
5 CXGN::Map - classes to get information on SGN mapping information and to add new map and map version data (new_map, store, & map_version functions).
7 =head1 DESCRIPTION
9 This class was originally written to retrieve data on genetic maps in the SGN database. However, map types multiplied and this class was re-written as a factory object producing a map object of the appropriate type - genetic, fish, individual, user, etc. These map objects are defined in the CXGN::Map:: namespace. Previous documentation mentioned the existence of a CXGN::Map::Storable class, however, this never seemed to exist and the new map interface and subclasses have been written as read/write objects.
11 The "new" function has been re-cast to act as a factory object and will produce the right type of Map object given the appropriate parameters, which are defined as follows:
13 parameter map type
14 --------- --------
15 map_id genetic or fish
16 map_version_id genetic or fish
17 user_map_id user_map
18 population_id IL map
19 individual_id indivdual_map
21 Note that much of the functionality of this class has been factored out into a CXGN::LinkageGroup object, which also exists in different incarnations for the different map types.
23 =head1 AUTHORS
25 John Binns <zombieite@gmail.com>, Lukas Mueller (lam87@cornell.edu) and Isaak Y Tecle (iyt2@cornell.edu)
29 =head1 FUNCTIONS
31 This class defines the following functions to be implemented by the subclasses, and keeps the old functions for compatibility (see deprecated functions below).
33 =cut
36 use strict;
37 use warnings;
39 package CXGN::Map;
41 use CXGN::DB::Connection;
42 use CXGN::Map::Version;
44 use base "CXGN::DB::Object";
46 =head2 new
48 Usage: my $map = CXGN::Map->new($dbh, {map_version_id=>30})
49 Desc: creates a new CXGN::Map object
50 Ret:
51 Args: - a database handle, if possible using
52 CXGN::DB::Connection object
53 - a hashref, containing either a key map_id or a key
54 map_version_id, but not both!
55 Side Effects:
56 Example:
58 =cut
60 sub new {
61 my $class=shift;
62 my($dbh,$map_info)=@_;
63 my $self=$class->SUPER::new($dbh);
64 unless(CXGN::DB::Connection::is_valid_dbh($dbh)){die"Invalid DBH";}
65 ref($map_info) eq 'HASH' or die"Must send in a dbh and hash ref with a map_id key or a map_version_id key";
66 $self->{map_version_id}=$map_info->{map_version_id};
67 $self->{map_id}=$map_info->{map_id};
69 my $map_id_t = $self->{map_id};
70 #print STDERR "map id: $map_id_t from map object\n";
71 if($self->{map_id})
73 if($self->{map_version_id})
75 die"You must only send in a map_id or a map_version_id, not both";
77 my $map_version_id_q=$dbh->prepare("SELECT map_version_id
78 FROM map_version
79 WHERE map_id=?
80 AND current_version='t'"
82 $map_version_id_q->execute($self->{map_id});
83 if (my @row = $map_version_id_q->fetchrow_array()) {
84 $self->{map_version_id} = $row[0];
85 } else {
86 print STDERR "ERROR no map_version_id\n";
89 $self->{map_version_id} or return undef;
90 my $general_info_q=$dbh->prepare
92 select
93 map_id,
94 map_version_id,
95 date_loaded,
96 current_version,
97 short_name,
98 long_name,
99 abstract,
100 map_type,
101 population_id,
102 has_IL,
103 has_physical
104 from
105 map_version
106 inner join map using (map_id)
107 where
108 map_version_id=?
110 $general_info_q->execute($self->{map_version_id});
112 $self->{map_id},
113 $self->{map_version_id},
114 $self->{date_loaded},
115 $self->{current_version},
116 $self->{short_name},
117 $self->{long_name},
118 $self->{abstract},
119 $self->{map_type},
120 $self->{population_id},
121 $self->{has_IL},
122 $self->{has_physical}
124 )=$general_info_q->fetchrow_array();
125 if(!$self->{map_version_id}){return undef;}
126 my $linkage_q=$dbh->prepare('SELECT linkage_group.lg_id AS lg_id,linkage_group.map_version_id AS map_version_id,
127 lg_order,lg_name, min(position) AS north_centromere, MAX(position) AS south_centromere
128 FROM linkage_group
129 LEFT JOIN marker_location ON (north_location_id=location_id
130 OR south_location_id=location_id)
131 WHERE linkage_group.map_version_id=?
132 GROUP BY linkage_group.lg_id, linkage_group.map_version_id,
133 lg_order, lg_name order by lg_order');
134 $linkage_q->execute($self->{map_version_id});
135 while(my $linkage_group=$linkage_q->fetchrow_hashref())
137 push(@{$self->{linkage_groups}},$linkage_group);
139 return $self;
142 sub store {
143 my $self = shift;
145 my $map_id = $self->get_map_id();
146 print STDERR "map id from store: $map_id\n";
147 if ($map_id) {
148 my $sth = $self->get_dbh()->prepare("UPDATE sgn.map SET
149 short_name = ?,
150 long_name = ?,
151 abstract = ?,
152 map_type = ?,
153 parent1_stock_id = ?,
154 parent2_stock_id = ?,
155 units = ?,
156 population_stock_id = ?
157 WHERE map_id = ?"
159 $sth->execute($self->{short_name},
160 $self->{long_name},
161 $self->{abstract},
162 $self->{map_type},
163 $self->{parent1_stock_id},
164 $self->{parent2_stock_id},
165 $self->get_units(),
166 $self->{population_stock_id},
167 $map_id
170 print STDERR "Storing map data... \n";
171 print STDERR "updated map id: $map_id\n";
172 #$dbh->last_insert_id("map", "sgn");
173 return $map_id;
175 } else {
176 print STDERR "No map id\n";
177 return 0;
183 sub new_map {
184 my $self=shift;
185 my $dbh = shift;
186 my $name = shift;
187 my ($map_id, $sth);
189 print STDERR "Short map name: $name\n";
190 if ($name) {
191 $sth = $dbh->prepare("SELECT map_id
192 FROM sgn.map
193 WHERE short_name ILIKE ?"
195 $sth->execute($name);
196 if (my @row = $sth->fetchrow_array) {
197 $map_id = $row[0];
198 } else {
199 print STDERR "Error: No Map Id for $name\n";
202 else {
203 print STDERR "Provide map name, please.\n";
204 die "No map name provided!\n";
207 unless ($map_id) {
208 $sth = $dbh->prepare("INSERT INTO sgn.map (short_name, map_type) VALUES (?, 'genetic') RETURNING map_id");
209 $sth->execute($name) or die "ERROR can not create map\n";;
210 ($map_id) = $sth->fetchrow_array() or die "ERROR inserting map\n";
211 print STDERR "stored new Map Id: $map_id\n";
214 my ($map, $map_version_id);
215 if ($map_id) {
216 $map_version_id = CXGN::Map::Version->map_version($dbh, $map_id);
217 #$map_version_id= $self->map_version($dbh, $map_id);
218 print STDERR "created map version_id: $map_version_id for map_id: $map_id\n";
219 $map = CXGN::Map->new($dbh, {map_id=>$map_id});
220 my $new_map_id = $map->{map_id};
221 print STDERR "new_map function with map_id = $new_map_id.\n";
226 return $map;
231 =head2 accessors set_short_name, get_short_name
233 Property:
234 Setter Args:
235 Getter Args:
236 Getter Ret:
237 Side Effects:
238 Description:
240 =cut
242 sub get_short_name {
243 my $self=shift;
244 return $self->{short_name};
247 sub set_short_name {
248 my $self=shift;
249 $self->{short_name}=shift;
252 =head2 accessors set_long_name, get_long_name
254 Property:
255 Setter Args:
256 Getter Args:
257 Getter Ret:
258 Side Effects:
259 Description:
261 =cut
263 sub get_long_name {
264 my $self=shift;
265 return $self->{long_name};
268 sub set_long_name {
269 my $self=shift;
270 $self->{long_name}=shift;
273 =head2 accessors set_abstract, get_abstract
275 Property:
276 Setter Args:
277 Getter Args:
278 Getter Ret:
279 Side Effects:
280 Description:
282 =cut
284 sub get_abstract {
285 my $self=shift;
286 return $self->{abstract};
289 sub set_abstract {
290 my $self=shift;
291 $self->{abstract}=shift;
295 =head2 accessors get_parent_1, set_parent_1
297 DEPRECATED
298 Usage:
299 Desc:
300 Property
301 Side Effects:
302 Example:
304 =cut
306 sub get_parent_1 {
307 my $self = shift;
308 return $self->{parent_1};
311 sub set_parent_1 {
312 my $self = shift;
313 $self->{parent_1} = shift;
316 =head2 accessors get_parent1_stock_id, set_parent1_stock_id
318 Usage:
319 Desc: sets the stock id of parent 1 of this map.
320 Property
321 Side Effects:
322 Example:
324 =cut
326 sub get_parent1_stock_id {
327 my $self = shift;
328 return $self->{parent1_stock_id};
331 sub set_parent1_stock_id {
332 my $self = shift;
333 $self->{parent1_stock_id} = shift;
336 =head2 accessors get_parent2_stock_id, set_parent2_stock_id
338 Usage:
339 Desc: sets the stock id of the parent 2 of this map.
340 Property
341 Side Effects:
342 Example:
344 =cut
346 sub get_parent2_stock_id {
347 my $self = shift;
348 return $self->{parent2_stock_id};
351 sub set_parent2_stock_id {
352 my $self = shift;
353 $self->{parent2_stock_id} = shift;
356 =head2 accessors get_population_stock_id, set_population_stock_id
358 Usage:
359 Desc: sets the population id of the map, referencing
360 stock table.
361 Property
362 Side Effects:
363 Example:
365 =cut
367 sub get_population_stock_id {
368 my $self = shift;
369 return $self->{population_stock_id};
372 sub set_population_stock_id {
373 my $self = shift;
374 $self->{population_stock_id} = shift;
377 =head2 accessors get_population_id, set_population_id
379 DEPRECATED.
380 Usage:
381 Desc:
382 Property
383 Side Effects:
384 Example:
386 =cut
388 sub get_population_id {
389 my $self = shift;
390 return $self->{population_id};
393 sub set_population_id {
394 my $self = shift;
395 $self->{population_id} = shift;
398 =head2 get_map_id
400 Usage:
401 Desc:
402 Ret:
403 Args:
404 Side Effects:
405 Example:
407 =cut
410 sub set_map_id {
411 my $self = shift;
412 $self->{map_id}=shift;
414 sub get_map_id {
415 my $self = shift;
416 return $self->{map_id};
420 =head2 accessors set_linkage_groups, get_linkage_groups
422 Property:
423 Setter Args:
424 Getter Args:
425 Getter Ret:
426 Side Effects:
427 Description:
429 =cut
431 sub get_linkage_groups {
432 my $self=shift;
433 return @{$self->{linkage_groups}};
436 sub set_linkage_groups {
437 my $self=shift;
438 @{$self->{linkage_groups}}=@_;
441 =head2 function add_linkage_group
443 Synopsis:
444 Arguments:
445 Returns:
446 Side effects:
447 Description:
449 =cut
451 sub add_linkage_group {
452 my $self = shift;
453 my $lg = shift;
454 push @{$self->{linkage_groups}}, $lg;
458 =head2 accessors set_map_type, get_map_type
460 Property:
461 Setter Args:
462 Getter Args:
463 Getter Ret:
464 Side Effects:
465 Description:
467 =cut
469 sub get_map_type {
470 my $self=shift;
471 return $self->{map_type};
474 sub set_map_type {
475 my $self=shift;
476 $self->{map_type}=shift;
480 =head2 function get_units
482 Synopsis:
483 Arguments:
484 Returns:
485 Side effects:
486 Description:
488 =cut
490 sub get_units {
491 my $self=shift;
492 if ($self->get_map_type() eq "genetic") {
493 return "cM";
495 elsif ($self->get_map_type() eq "fish") {
496 return "%";
498 elsif ($self->get_map_type() =~ /sequenc/) {
499 return "MB";
501 elsif ($self->get_map_type() =~ /qtl/i) {
502 return "cM";
504 else {
505 return "unknown";
514 =head1 DEPRECATED FUNCTIONS
516 These functions are still working but should not be used in new code.
518 Note that these functions only work as getters and not as setters.
520 =cut
522 =head2 function map_id
524 Synopsis:
525 Arguments:
526 Returns:
527 Side effects:
528 Description:
530 =cut
533 sub map_id {
534 my $self=shift;
535 return $self->{map_id};
538 =head2 function map_version_id
540 Synopsis:
541 Arguments:
542 Returns:
543 Side effects:
544 Description:
546 =cut
548 sub map_version_id {
549 my $self=shift;
550 return $self->{map_version_id};
553 =head2 function short_name
555 Synopsis:
556 Arguments:
557 Returns:
558 Side effects:
559 Description:
561 =cut
563 sub short_name {
564 my $self=shift;
565 return $self->{short_name};
568 =head2 function long_name
570 Synopsis:
571 Arguments:
572 Returns:
573 Side effects:
574 Description:
576 =cut
578 sub long_name {
579 my $self=shift;
580 return $self->{long_name};
583 =head2 function abstract
585 Synopsis:
586 Arguments:
587 Returns:
588 Side effects:
589 Description:
591 =cut
593 sub abstract {
594 my $self=shift;
595 return $self->{abstract};
598 =head2 linkage_groups
600 Usage:
601 Desc:
602 Ret: a reference to an array of hashrefs with linkage group info.
603 hash keys include lg_name and lg_order
604 Args:
605 Side Effects:
606 Example:
608 =cut
610 sub linkage_groups {
611 my $self=shift;
612 if($self->{linkage_groups})
614 return $self->{linkage_groups};
616 else
618 return [];
622 =head2 map_type
624 Usage:
625 Desc:
626 Ret: the type of the map, either 'fish' for a fish map
627 or 'genetic' for a genetic map.
628 Args:
629 Side Effects:
630 Example:
632 =cut
634 sub map_type {
635 my $self = shift;
636 return $self->{map_type};
639 =head2 has_IL
641 Usage:
642 Desc:
643 Ret:
644 Args:
645 Side Effects:
646 Example:
648 =cut
650 sub has_IL {
651 my $self = shift;
652 return $self->{has_IL};
655 =head2 has_physical
657 Usage:
658 Desc:
659 Ret:
660 Args:
661 Side Effects:
662 Example:
664 =cut
666 sub has_physical {
667 my $self = shift;
668 return $self->{has_physical};
674 =head2 get_chr_names
676 Usage:
677 Desc: a shortcut function to get at the chromosome names,
678 sorted by lg_order
679 Ret: a list of chromosome names.
680 Args:
681 Side Effects:
682 Example:
684 =cut
686 sub get_chr_names {
687 my $self = shift;
688 my $linkage_groups_ref = $self->linkage_groups();
689 my @names = map $_->{lg_name}, @{$linkage_groups_ref};
690 return @names;
693 =head2 has_linkage_group
695 Usage:
696 Desc:
697 Ret: 1 if the string or number represents a linkage group
698 of this map
699 0 if it doesn\'t
700 Args: a string or number describing a possible linkage
701 group of this map
702 Side Effects:
703 Example:
705 =cut
707 sub has_linkage_group {
708 my $self = shift;
709 my $candidate = shift;
710 chomp($candidate);
711 $candidate=~ s/\s*(.*)\s*/$1/;
712 foreach my $n (map $_->{lg_name} , @{$self->linkage_groups()}) {
713 #print STDERR "comparing $n with $candidate...\n";
714 if ($candidate =~ /^$n$/i) {
715 #print STDERR "Yip!\n";
716 return 1;
719 return 0;
722 =head2 function get_centromere
724 Synopsis: my ($north, $south, $center) = $map->get_centromere($lg_name)
725 Arguments: a valid linkage group name
726 Returns: a three member list, the first element corresponds
727 to the north boundary of the centromere in cM
728 the second corresponds to the south boundary of
729 the centromere in cM, the third is the arithmetic mean
730 of the two first values.
731 Side effects: none
732 Description:
734 =cut
736 sub get_centromere {
737 my $self=shift;
738 my $lg = shift;
740 if (! $self->has_linkage_group($lg)) {
741 die "Not a valid linkage group for this map!\n";
744 my $lg_hash = $self->get_linkage_group_hash($lg);
745 # foreach my $k (keys %$lg_hash) {
746 # print " $k, $lg_hash->{$k}\n";
748 my $north = $lg_hash->{north_centromere} || 0;
749 my $south = $lg_hash->{south_centromere} || 0;
750 return ($north, $south, int(($north+$south)/2));
753 sub get_linkage_group_hash {
754 my $self= shift;
755 my $lg_name = shift;
756 foreach my $lg_hash (@{$self->linkage_groups()}) {
757 if ($lg_hash->{lg_name} eq $lg_name) {
758 return $lg_hash;