my ($self, $sth, $mapper, $dest_slice) = @_;
#
# This code is ugly because an attempt has been made to remove as many
# function calls as possible for speed purposes. Thus many caches and
# a fair bit of gymnastics is used.
#
my $sa = $self->db()->get_SliceAdaptor();
my $msa = $self->db()->get_MiscSetAdaptor();
my @features;
my %ms_hash;
my %slice_hash;
my %sr_name_hash;
my %sr_cs_hash;
my(
$misc_feature_id, $seq_region_id, $seq_region_start,
$seq_region_end, $seq_region_strand,
$attrib_value, $attrib_type_code, $misc_set_id,
$attrib_type_name, $attrib_type_description );
$sth->bind_columns( \$misc_feature_id, \$seq_region_id, \$seq_region_start,
\$seq_region_end, \$seq_region_strand,
\$attrib_value, \$attrib_type_code,\$misc_set_id,
\$attrib_type_name, \$attrib_type_description );
my $dest_slice_start;
my $dest_slice_end;
my $dest_slice_strand;
my $dest_slice_length;
my $dest_slice_cs;
my $dest_slice_sr_name;
my $dest_slice_sr_id;
my $asma;
if ($dest_slice) {
$dest_slice_start = $dest_slice->start();
$dest_slice_end = $dest_slice->end();
$dest_slice_strand = $dest_slice->strand();
$dest_slice_length = $dest_slice->length();
$dest_slice_cs = $dest_slice->coord_system();
$dest_slice_sr_name = $dest_slice->seq_region_name();
$dest_slice_sr_id = $dest_slice->get_seq_region_id();
$asma = $self->db->get_AssemblyMapperAdaptor();
}
my $current = -1;
my $throw_away = -1;
my $feat;
my $feat_misc_sets;
my $feat_attribs;
my $seen_attribs;
FEATURE: while($sth->fetch()) {
#if this feature is not being used, skip all rows related to it
next if($throw_away == $misc_feature_id);
if ($current == $misc_feature_id) {
#still working on building up attributes and sets for current feature
#if there is a misc_set, add it to the current feature
if ($misc_set_id) {
my $misc_set = $ms_hash{$misc_set_id} ||=
$msa->fetch_by_dbID($misc_set_id);
if ( ! exists $feat_misc_sets->{$misc_set->{'code'}} ) {
$feat->add_MiscSet( $misc_set );
$feat_misc_sets->{$misc_set->{'code'}} = $misc_set;
}
}
#if there is a new attribute add it to the current feature
if ($attrib_value && $attrib_type_code && !$seen_attribs->{"$attrib_type_code:$attrib_value"}) {
( -CODE => $attrib_type_code,
-NAME => $attrib_type_name,
-DESC => $attrib_type_description,
-VALUE => $attrib_value
);
$feat_attribs ||= [];
push @$feat_attribs, $attrib;
$seen_attribs->{"$attrib_type_code:$attrib_value"} = 1;
}
} else {
if ($feat) {
#start working on a new feature, discard references to last one
$feat = {};
$feat_attribs = [];
$feat_misc_sets = {};
$seen_attribs = {};
}
$current = $misc_feature_id;
#need to get the internal_seq_region, if present
$seq_region_id = $self->get_seq_region_id_internal($seq_region_id);
my $slice = $slice_hash{"ID:".$seq_region_id};
if (!$slice) {
$slice = $sa->fetch_by_seq_region_id($seq_region_id);
$slice_hash{"ID:".$seq_region_id} = $slice;
$sr_name_hash{$seq_region_id} = $slice->seq_region_name();
$sr_cs_hash{$seq_region_id} = $slice->coord_system();
}
if(!$mapper && $dest_slice && !$dest_slice_cs->equals($slice->coord_system)) {
$mapper = $asma->fetch_by_CoordSystems($dest_slice_cs, $slice->coord_system);
}
my $sr_name = $sr_name_hash{$seq_region_id};
my $sr_cs = $sr_cs_hash{$seq_region_id};
#
# remap the feature coordinates to another coord system
# if a mapper was provided
#
if ($mapper) {
if (defined $dest_slice && $mapper->isa('Bio::EnsEMBL::ChainedAssemblyMapper') ) {
($seq_region_id, $seq_region_start,$seq_region_end, $seq_region_strand) =
$mapper->map($sr_name, $seq_region_start, $seq_region_end, $seq_region_strand, $sr_cs, 1, $dest_slice);
} else {
($seq_region_id, $seq_region_start,$seq_region_end, $seq_region_strand) =
$mapper->fastmap($sr_name, $seq_region_start, $seq_region_end,$seq_region_strand, $sr_cs);
}
#skip features that map to gaps or coord system boundaries
if(!defined($seq_region_id)) {
$throw_away = $misc_feature_id;
next FEATURE;
}
#get a slice in the coord system we just mapped to
$slice = $slice_hash{"ID:".$seq_region_id} ||= $sa->fetch_by_seq_region_id($seq_region_id);
}
#
# If a destination slice was provided convert the coords
#
if ($dest_slice) {
my $seq_region_len = $dest_slice->seq_region_length();
if ($dest_slice_strand == 1) { # Positive strand
$seq_region_start = $seq_region_start - $dest_slice_start + 1;
$seq_region_end = $seq_region_end - $dest_slice_start + 1;
if ($dest_slice->is_circular()) {
# Handle cicular chromosomes.
if ($seq_region_start > $seq_region_end) {
# Looking at a feature overlapping the chromsome origin.
if ($seq_region_end > $dest_slice_start) {
# Looking at the region in the beginning of the chromosome.
$seq_region_start -= $seq_region_len;
}
if ($seq_region_end < 0) {
$seq_region_end += $seq_region_len;
}
} else {
if ($dest_slice_start > $dest_slice_end && $seq_region_end < 0) {
# Looking at the region overlapping the chromosome
# origin and a feature which is at the beginning of the
# chromosome.
$seq_region_start += $seq_region_len;
$seq_region_end += $seq_region_len;
}
}
} ## end if ($dest_slice->is_circular...)
} else { # Negative strand
my $start = $dest_slice_end - $seq_region_end + 1;
my $end = $dest_slice_end - $seq_region_start + 1;
if ($dest_slice->is_circular()) {
if ($dest_slice_start > $dest_slice_end) {
# slice spans origin or replication
if ($seq_region_start >= $dest_slice_start) {
$end += $seq_region_len;
$start += $seq_region_len if $seq_region_end > $dest_slice_start;
} elsif ($seq_region_start <= $dest_slice_end) {
# do nothing
} elsif ($seq_region_end >= $dest_slice_start) {
$start += $seq_region_len;
$end += $seq_region_len;
} elsif ($seq_region_end <= $dest_slice_end) {
$end += $seq_region_len if $end < 0;
} elsif ($seq_region_start > $seq_region_end) {
$end += $seq_region_len;
}
} else {
if ($seq_region_start <= $dest_slice_end and $seq_region_end >= $dest_slice_start) {
# do nothing
} elsif ($seq_region_start > $seq_region_end) {
if ($seq_region_start <= $dest_slice_end) {
$start -= $seq_region_len;
} elsif ($seq_region_end >= $dest_slice_start) {
$end += $seq_region_len;
}
}
}
}
$seq_region_start = $start;
$seq_region_end = $end;
$seq_region_strand *= -1;
} ## end else [ if ($dest_slice_strand...)]
#throw away features off the end of the requested slice
if ($seq_region_end < 1 || $seq_region_start > $dest_slice_length ||
$dest_slice_sr_id != $seq_region_id) {
#flag this feature as one to throw away
$throw_away = $misc_feature_id;
next FEATURE;
}
$slice = $dest_slice;
}
if ($attrib_value && $attrib_type_code) {
( -CODE => $attrib_type_code,
-NAME => $attrib_type_name,
-DESC => $attrib_type_description,
-VALUE => $attrib_value
);
$feat_attribs = [$attrib];
$seen_attribs->{"$attrib_type_code:$attrib_value"} = 1;
}
$feat =
$self->_create_feature_fast( 'Bio::EnsEMBL::MiscFeature', {
'start' => $seq_region_start,
'end' => $seq_region_end,
'strand' => $seq_region_strand,
'slice' => $slice,
'adaptor' => $self,
'dbID' => $misc_feature_id,
'attributes' => $feat_attribs ||= []
} );
push @features, $feat;
if ($misc_set_id) {
#get the misc_set object
my $misc_set = $ms_hash{$misc_set_id} ||=
$msa->fetch_by_dbID($misc_set_id);
if ( ! exists $feat_misc_sets->{$misc_set->{'code'}} ) {
$feat->add_MiscSet( $misc_set );
$feat_misc_sets->{$misc_set->{'code'}} = $misc_set;
}
}
}
}
return \@features;
}