// AnnotateOne annotates a relatable with the Sources in an Annotator. // In most cases, no need to specify end (it should always be a single // arugment indicting LEFT, RIGHT, or INTERVAL, used from AnnotateEnds func (a *Annotator) AnnotateOne(r interfaces.Relatable, strict bool, end ...string) error { if len(r.Related()) == 0 { return nil } prefix := "" if len(end) > 0 { prefix = end[0] if len(end) > 1 { log.Fatalf("too many ends in AnnotateOne") } } parted := a.partition(r) var v interfaces.IVariant v, ok := r.(interfaces.IVariant) if !ok { log.Fatal("can't annotate non-IVariant", r) } var src *Source for i := range a.Sources { src = a.Sources[i] if len(parted) <= src.Index { continue } related := parted[src.Index] if len(related) == 0 { continue } vals := collect(v, related, src, strict) src.AnnotateOne(v, vals, prefix) } return nil }
// partition separates the relateds for a relatable so it reduces running over the data multiple times for each file. func (a *Annotator) partition(r interfaces.Relatable) [][]interfaces.Relatable { parted := make([][]interfaces.Relatable, 0, 0) for _, o := range r.Related() { s := int(o.Source()) - 1 for len(parted) <= s { parted = append(parted, make([]interfaces.Relatable, 0)) } parted[s] = append(parted[s], o) } return parted }
// AnnotateEnds makes a new 1-base interval for the left and one for the right end // so that it can use the same machinery to annotate the ends and the entire interval. // Output into the info field is prefixed with "left_" or "right_". func (a *Annotator) AnnotateEnds(v interfaces.Relatable, ends string) error { var err error // if Both, call the interval, left, and right version to annotate. if ends == BOTH { if e := a.AnnotateOne(v, a.Strict); e != nil { err = e } if e := a.PostAnnotate(v.Chrom(), int(v.Start()), int(v.End()), v.(interfaces.IVariant).Info(), ""); e != nil { err = e } if e := a.AnnotateEnds(v, LEFT); e != nil { err = e } if e := a.AnnotateEnds(v, RIGHT); e != nil { err = e } } if ends == INTERVAL { err := a.AnnotateOne(v, a.Strict) err2 := a.PostAnnotate(v.Chrom(), int(v.Start()), int(v.End()), v.(interfaces.IVariant).Info(), "") if err != nil { return err } return err2 } // hack: // modify the variant in-place to create a 1-base variant at the end of // the interval. annotate that end and then change the position back to what it was. if ends == LEFT || ends == RIGHT { // the end is determined by the SVLEN, so we have to make sure it has length 1. variant := v.(*parsers.Variant).IVariant var l, r uint32 var ok bool if ends == LEFT { l, r, ok = variant.CIPos() } else { l, r, ok = variant.CIEnd() } // dont reannotate same interval if !ok && (l == v.Start() && r == v.End()) { return nil } m := vcfgo.NewInfoByte([]byte(fmt.Sprintf("SVLEN=%d;END=%d", r-l-1, r)), variant.(*vcfgo.Variant).Header) v2 := parsers.NewVariant(&vcfgo.Variant{Chromosome: v.Chrom(), Pos: uint64(l + 1), Reference: "A", Alternate: []string{"<DEL>"}, Info_: m}, v.Source(), v.Related()) err = a.AnnotateOne(v2, false, ends) var val interface{} for _, key := range v2.Info().Keys() { if key == "SVLEN" || key == "END" { continue } val, err = v2.Info().Get(key) variant.Info().Set(key, val) } err2 := a.PostAnnotate(v.Chrom(), int(l), int(r), variant.Info(), ends) if err2 != nil { err = err2 } } return err }