fixed recursive_children cvterm function, and added tests for parents and children
[cxgn-corelibs.git] / lib / CXGN / Map.pm
blob01527065afa95ed8a66c063f7aeaacfdcfa8fc2b
2 =head1 NAME
4 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).
6 =head1 DESCRIPTION
8 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.
10 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:
12 parameter map type
13 --------- --------
14 map_id genetic or fish
15 map_version_id genetic or fish
16 user_map_id user_map
17 population_id IL map
18 individual_id indivdual_map
20 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.
22 =head1 AUTHORS
24 John Binns <zombieite@gmail.com>, Lukas Mueller (lam87@cornell.edu) and Isaak Y Tecle (iyt2@cornell.edu)
28 =head1 FUNCTIONS
30 This class defines the following functions to be implemented by the subclasses, and keeps the old functions for compatibility (see deprecated functions below).
32 =cut
34 use strict;
35 use CXGN::DB::Connection;
36 use CXGN::Map::Version;
37 package CXGN::Map;
39 =head2 new
41 Usage: my $map = CXGN::Map->new($dbh, {map_version_id=>30})
42 Desc: creates a new CXGN::Map object
43 Ret:
44 Args: - a database handle, if possible using
45 CXGN::DB::Connection object
46 - a hashref, containing either a key map_id or a key
47 map_version_id, but not both!
48 Side Effects:
49 Example:
51 =cut
53 sub new {
54 my $class=shift;
55 my($dbh,$map_info)=@_;
56 my $self=bless({},$class);
57 unless(CXGN::DB::Connection::is_valid_dbh($dbh)){die"Invalid DBH";}
58 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";
59 $self->{map_version_id}=$map_info->{map_version_id};
60 $self->{map_id}=$map_info->{map_id};
62 my $map_id_t = $self->{map_id};
63 #print STDERR "map id: $map_id_t from map object\n";
64 if($self->{map_id})
66 if($self->{map_version_id})
68 die"You must only send in a map_id or a map_version_id, not both";
70 my $map_version_id_q=$dbh->prepare("SELECT map_version_id
71 FROM map_version
72 WHERE map_id=?
73 AND current_version='t'"
75 $map_version_id_q->execute($self->{map_id});
76 ($self->{map_version_id})=$map_version_id_q->fetchrow_array();
78 $self->{map_version_id} or return undef;
79 my $general_info_q=$dbh->prepare
81 select
82 map_id,
83 map_version_id,
84 date_loaded,
85 current_version,
86 short_name,
87 long_name,
88 abstract,
89 map_type,
90 population_id,
91 has_IL,
92 has_physical
93 from
94 map_version
95 inner join map using (map_id)
96 where
97 map_version_id=?
98 ');
99 $general_info_q->execute($self->{map_version_id});
101 $self->{map_id},
102 $self->{map_version_id},
103 $self->{date_loaded},
104 $self->{current_version},
105 $self->{short_name},
106 $self->{long_name},
107 $self->{abstract},
108 $self->{map_type},
109 $self->{population_id},
110 $self->{has_IL},
111 $self->{has_physical}
113 )=$general_info_q->fetchrow_array();
114 if(!$self->{map_version_id}){return undef;}
115 my $linkage_q=$dbh->prepare('SELECT linkage_group.lg_id AS lg_id,linkage_group.map_version_id AS map_version_id,
116 lg_order,lg_name, min(position) AS north_centromere, MAX(position) AS south_centromere
117 FROM linkage_group
118 LEFT JOIN marker_location ON (north_location_id=location_id
119 OR south_location_id=location_id)
120 WHERE linkage_group.map_version_id=?
121 GROUP BY linkage_group.lg_id, linkage_group.map_version_id,
122 lg_order, lg_name order by lg_order');
123 $linkage_q->execute($self->{map_version_id});
124 while(my $linkage_group=$linkage_q->fetchrow_hashref())
126 push(@{$self->{linkage_groups}},$linkage_group);
128 return $self;
131 sub store {
132 my $self = shift;
133 my $dbh = CXGN::DB::Connection->new();
134 my $map_id = $self->get_map_id();
135 print STDERR "map id from store: $map_id\n";
136 if ($map_id) {
137 my $sth = $dbh->prepare("UPDATE sgn.map SET
138 short_name = ?,
139 long_name = ?,
140 abstract = ?,
141 map_type = ?,
142 parent_1 = ?,
143 parent_2 = ?,
144 units = ?,
145 population_id = ?
146 WHERE map_id = ?"
148 $sth->execute($self->{short_name},
149 $self->{long_name},
150 $self->{abstract},
151 $self->{map_type},
152 $self->{parent_1},
153 $self->{parent_2},
154 $self->get_units(),
155 $self->{population_id},
156 $map_id
159 print STDERR "Storing map data... \n";
160 print STDERR "updated map id: $map_id\n";
161 #$dbh->last_insert_id("map", "sgn");
162 return $map_id;
164 } else {
165 print STDERR "No map id\n";
166 return 0;
172 sub new_map {
173 my $self=shift;
174 my $dbh = shift;
175 my $name = shift;
176 my ($map_id, $sth);
178 print STDERR "Short map name: $name\n";
179 if ($name) {
180 $sth = $dbh->prepare("SELECT map_id
181 FROM sgn.map
182 WHERE short_name ILIKE ?"
184 $sth->execute($name);
185 $map_id = $sth->fetchrow_array();
186 print STDERR "Map Id: $map_id\n";
188 else {
189 print STDERR "Provide map name, please.\n";
190 die "No map name provided!\n";
193 unless ($map_id) {
194 $sth = $dbh->prepare("INSERT INTO sgn.map (short_name) VALUES (?)");
195 $sth->execute($name);
196 $map_id = $dbh->last_insert_id("map", "sgn");
197 print STDERR "stored new Map Id: $map_id\n";
200 my ($map, $map_version_id);
201 if ($map_id) {
202 $map_version_id = CXGN::Map::Version->map_version($dbh, $map_id);
203 #$map_version_id= $self->map_version($dbh, $map_id);
204 print STDERR "created map version_id: $map_version_id for map_id: $map_id\n";
205 $map = CXGN::Map->new($dbh, {map_id=>$map_id});
206 my $new_map_id = $map->{map_id};
207 print STDERR "new_map function with map_id = $new_map_id.\n";
212 return $map;
217 =head2 accessors set_short_name, get_short_name
219 Property:
220 Setter Args:
221 Getter Args:
222 Getter Ret:
223 Side Effects:
224 Description:
226 =cut
228 sub get_short_name {
229 my $self=shift;
230 return $self->{short_name};
233 sub set_short_name {
234 my $self=shift;
235 $self->{short_name}=shift;
238 =head2 accessors set_long_name, get_long_name
240 Property:
241 Setter Args:
242 Getter Args:
243 Getter Ret:
244 Side Effects:
245 Description:
247 =cut
249 sub get_long_name {
250 my $self=shift;
251 return $self->{long_name};
254 sub set_long_name {
255 my $self=shift;
256 $self->{long_name}=shift;
259 =head2 accessors set_abstract, get_abstract
261 Property:
262 Setter Args:
263 Getter Args:
264 Getter Ret:
265 Side Effects:
266 Description:
268 =cut
270 sub get_abstract {
271 my $self=shift;
272 return $self->{abstract};
275 sub set_abstract {
276 my $self=shift;
277 $self->{abstract}=shift;
281 =head2 accessors get_parent_1, set_parent_1
283 Usage:
284 Desc:
285 Property
286 Side Effects:
287 Example:
289 =cut
291 sub get_parent_1 {
292 my $self = shift;
293 return $self->{parent_1};
296 sub set_parent_1 {
297 my $self = shift;
298 $self->{parent_1} = shift;
300 =head2 accessors get_population_id, set_population_id
302 Usage:
303 Desc:
304 Property
305 Side Effects:
306 Example:
308 =cut
310 sub get_population_id {
311 my $self = shift;
312 return $self->{population_id};
315 sub set_population_id {
316 my $self = shift;
317 $self->{population_id} = shift;
320 =head2 get_map_id
322 Usage:
323 Desc:
324 Ret:
325 Args:
326 Side Effects:
327 Example:
329 =cut
332 sub set_map_id {
333 my $self = shift;
334 $self->{map_id}=shift;
336 sub get_map_id {
337 my $self = shift;
338 return $self->{map_id};
344 =head2 accessors set_linkage_groups, get_linkage_groups
346 Property:
347 Setter Args:
348 Getter Args:
349 Getter Ret:
350 Side Effects:
351 Description:
353 =cut
355 sub get_linkage_groups {
356 my $self=shift;
357 return @{$self->{linkage_groups}};
360 sub set_linkage_groups {
361 my $self=shift;
362 @{$self->{linkage_groups}}=@_;
365 =head2 function add_linkage_group
367 Synopsis:
368 Arguments:
369 Returns:
370 Side effects:
371 Description:
373 =cut
375 sub add_linkage_group {
376 my $self = shift;
377 my $lg = shift;
378 push @{$self->{linkage_groups}}, $lg;
382 =head2 accessors set_map_type, get_map_type
384 Property:
385 Setter Args:
386 Getter Args:
387 Getter Ret:
388 Side Effects:
389 Description:
391 =cut
393 sub get_map_type {
394 my $self=shift;
395 return $self->{map_type};
398 sub set_map_type {
399 my $self=shift;
400 $self->{map_type}=shift;
404 =head2 function get_units
406 Synopsis:
407 Arguments:
408 Returns:
409 Side effects:
410 Description:
412 =cut
414 sub get_units {
415 my $self=shift;
416 if ($self->get_map_type() eq "genetic") {
417 return "cM";
419 elsif ($self->get_map_type() eq "fish") {
420 return "%";
422 elsif ($self->get_map_type() =~ /sequenc/) {
423 return "MB";
425 else {
426 return "unknown";
435 =head1 DEPRECATED FUNCTIONS
437 These functions are still working but should not be used in new code.
439 Note that these functions only work as getters and not as setters.
441 =cut
443 =head2 function map_id
445 Synopsis:
446 Arguments:
447 Returns:
448 Side effects:
449 Description:
451 =cut
454 sub map_id {
455 my $self=shift;
456 return $self->{map_id};
459 =head2 function map_version_id
461 Synopsis:
462 Arguments:
463 Returns:
464 Side effects:
465 Description:
467 =cut
469 sub map_version_id {
470 my $self=shift;
471 return $self->{map_version_id};
474 =head2 function short_name
476 Synopsis:
477 Arguments:
478 Returns:
479 Side effects:
480 Description:
482 =cut
484 sub short_name {
485 my $self=shift;
486 return $self->{short_name};
489 =head2 function long_name
491 Synopsis:
492 Arguments:
493 Returns:
494 Side effects:
495 Description:
497 =cut
499 sub long_name {
500 my $self=shift;
501 return $self->{long_name};
504 =head2 function abstract
506 Synopsis:
507 Arguments:
508 Returns:
509 Side effects:
510 Description:
512 =cut
514 sub abstract {
515 my $self=shift;
516 return $self->{abstract};
519 =head2 linkage_groups
521 Usage:
522 Desc:
523 Ret: a reference to an array of hashrefs with linkage group info.
524 hash keys include lg_name and lg_order
525 Args:
526 Side Effects:
527 Example:
529 =cut
531 sub linkage_groups {
532 my $self=shift;
533 if($self->{linkage_groups})
535 return $self->{linkage_groups};
537 else
539 return [];
543 =head2 map_type
545 Usage:
546 Desc:
547 Ret: the type of the map, either 'fish' for a fish map
548 or 'genetic' for a genetic map.
549 Args:
550 Side Effects:
551 Example:
553 =cut
555 sub map_type {
556 my $self = shift;
557 return $self->{map_type};
560 =head2 has_IL
562 Usage:
563 Desc:
564 Ret:
565 Args:
566 Side Effects:
567 Example:
569 =cut
571 sub has_IL {
572 my $self = shift;
573 return $self->{has_IL};
576 =head2 has_physical
578 Usage:
579 Desc:
580 Ret:
581 Args:
582 Side Effects:
583 Example:
585 =cut
587 sub has_physical {
588 my $self = shift;
589 return $self->{has_physical};
595 =head2 get_chr_names
597 Usage:
598 Desc: a shortcut function to get at the chromosome names,
599 sorted by lg_order
600 Ret: a list of chromosome names.
601 Args:
602 Side Effects:
603 Example:
605 =cut
607 sub get_chr_names {
608 my $self = shift;
609 my $linkage_groups_ref = $self->linkage_groups();
610 my @names = map $_->{lg_name}, @{$linkage_groups_ref};
611 return @names;
614 =head2 has_linkage_group
616 Usage:
617 Desc:
618 Ret: 1 if the string or number represents a linkage group
619 of this map
620 0 if it doesn\'t
621 Args: a string or number describing a possible linkage
622 group of this map
623 Side Effects:
624 Example:
626 =cut
628 sub has_linkage_group {
629 my $self = shift;
630 my $candidate = shift;
631 chomp($candidate);
632 $candidate=~ s/\s*(.*)\s*/$1/;
633 foreach my $n (map $_->{lg_name} , @{$self->linkage_groups()}) {
634 #print STDERR "comparing $n with $candidate...\n";
635 if ($candidate =~ /^$n$/i) {
636 #print STDERR "Yip!\n";
637 return 1;
640 return 0;
643 =head2 function get_centromere
645 Synopsis: my ($north, $south, $center) = $map->get_centromere($lg_name)
646 Arguments: a valid linkage group name
647 Returns: a three member list, the first element corresponds
648 to the north boundary of the centromere in cM
649 the second corresponds to the south boundary of
650 the centromere in cM, the third is the arithmetic mean
651 of the two first values.
652 Side effects: none
653 Description:
655 =cut
657 sub get_centromere {
658 my $self=shift;
659 my $lg = shift;
661 if (! $self->has_linkage_group($lg)) {
662 die "Not a valid linkage group for this map!\n";
665 my $lg_hash = $self->get_linkage_group_hash($lg);
666 # foreach my $k (keys %$lg_hash) {
667 # print " $k, $lg_hash->{$k}\n";
669 my $north = $lg_hash->{north_centromere};
670 my $south = $lg_hash->{south_centromere};
671 return ($north, $south, int(($north+$south)/2));
674 sub get_linkage_group_hash {
675 my $self= shift;
676 my $lg_name = shift;
677 foreach my $lg_hash (@{$self->linkage_groups()}) {
678 if ($lg_hash->{lg_name} eq $lg_name) {
679 return $lg_hash;
685 return 1;