mirror of
https://github.com/metabarcoding/obitools4.git
synced 2025-12-08 08:40:26 +00:00
First commit
This commit is contained in:
56
pkg/obitax/filter_on_name.go
Normal file
56
pkg/obitax/filter_on_name.go
Normal file
@@ -0,0 +1,56 @@
|
||||
package obitax
|
||||
|
||||
import (
|
||||
"regexp"
|
||||
)
|
||||
|
||||
func (taxonomy *Taxonomy) IFilterOnName(name string, strict bool) *ITaxonSet {
|
||||
if strict {
|
||||
nodes, ok := taxonomy.index[name]
|
||||
if ok {
|
||||
return nodes.Iterator()
|
||||
} else {
|
||||
empty := make(TaxonSet)
|
||||
return (&empty).Iterator()
|
||||
}
|
||||
}
|
||||
|
||||
return taxonomy.Iterator().IFilterOnName(name, strict)
|
||||
}
|
||||
|
||||
func (iterator *ITaxonSet) IFilterOnName(name string, strict bool) *ITaxonSet {
|
||||
new_iterator := NewITaxonSet()
|
||||
sentTaxa := make(map[int]bool)
|
||||
|
||||
if strict {
|
||||
go func() {
|
||||
for iterator.Next() {
|
||||
taxon := iterator.Get()
|
||||
if _, ok := sentTaxa[taxon.taxid]; !ok {
|
||||
if taxon.IsNameEqual(name) {
|
||||
sentTaxa[taxon.taxid] = true
|
||||
new_iterator.source <- taxon
|
||||
}
|
||||
}
|
||||
}
|
||||
close(new_iterator.source)
|
||||
}()
|
||||
} else {
|
||||
pattern := regexp.MustCompile(name)
|
||||
|
||||
go func() {
|
||||
for iterator.Next() {
|
||||
taxon := iterator.Get()
|
||||
if _, ok := sentTaxa[taxon.taxid]; !ok {
|
||||
if taxon.IsNameMatching(pattern) {
|
||||
sentTaxa[taxon.taxid] = true
|
||||
new_iterator.source <- taxon
|
||||
}
|
||||
}
|
||||
}
|
||||
close(new_iterator.source)
|
||||
}()
|
||||
}
|
||||
|
||||
return new_iterator
|
||||
}
|
||||
29
pkg/obitax/filter_on_rank.go
Normal file
29
pkg/obitax/filter_on_rank.go
Normal file
@@ -0,0 +1,29 @@
|
||||
package obitax
|
||||
|
||||
func (iterator *ITaxonSet) IFilterOnTaxRank(rank string) *ITaxonSet {
|
||||
new_iter := NewITaxonSet()
|
||||
|
||||
go func() {
|
||||
for iterator.Next() {
|
||||
taxon := iterator.Get()
|
||||
if taxon.rank == rank {
|
||||
new_iter.source <- taxon
|
||||
}
|
||||
}
|
||||
close(new_iter.source)
|
||||
}()
|
||||
|
||||
return new_iter
|
||||
}
|
||||
|
||||
func (set *TaxonSet) IFilterOnTaxRank(rank string) *ITaxonSet {
|
||||
return set.Iterator().IFilterOnTaxRank(rank)
|
||||
}
|
||||
|
||||
func (slice *TaxonSlice) IFilterOnTaxRank(rank string) *ITaxonSet {
|
||||
return slice.Iterator().IFilterOnTaxRank(rank)
|
||||
}
|
||||
|
||||
func (taxonomy *Taxonomy) IFilterOnTaxRank(rank string) *ITaxonSet {
|
||||
return taxonomy.Iterator().IFilterOnTaxRank(rank)
|
||||
}
|
||||
59
pkg/obitax/filter_on_subclade_of.go
Normal file
59
pkg/obitax/filter_on_subclade_of.go
Normal file
@@ -0,0 +1,59 @@
|
||||
package obitax
|
||||
|
||||
import "reflect"
|
||||
|
||||
func (iterator *ITaxonSet) IFilterOnSubcladeOf(taxon *TaxNode) *ITaxonSet {
|
||||
new_iter := NewITaxonSet()
|
||||
|
||||
go func() {
|
||||
for iterator.Next() {
|
||||
tx := iterator.Get()
|
||||
if tx.IsSubCladeOf(taxon) {
|
||||
new_iter.source <- tx
|
||||
}
|
||||
}
|
||||
close(new_iter.source)
|
||||
}()
|
||||
|
||||
return new_iter
|
||||
}
|
||||
|
||||
func (set *TaxonSet) IFilterOnSubcladeOf(taxon *TaxNode) *ITaxonSet {
|
||||
return set.Iterator().IFilterOnSubcladeOf(taxon)
|
||||
}
|
||||
|
||||
func (slice *TaxonSlice) IFilterOnSubcladeOf(taxon *TaxNode) *ITaxonSet {
|
||||
return slice.Iterator().IFilterOnSubcladeOf(taxon)
|
||||
}
|
||||
|
||||
func (taxonomy *Taxonomy) IFilterOnSubcladeOf(taxon *TaxNode) *ITaxonSet {
|
||||
return taxonomy.Iterator().IFilterOnSubcladeOf(taxon)
|
||||
}
|
||||
|
||||
func (iterator *ITaxonSet) IFilterBelongingSubclades(clades *TaxonSet) *ITaxonSet {
|
||||
|
||||
if len(*clades) == 0 {
|
||||
return iterator
|
||||
}
|
||||
|
||||
// Considers the second simplest case when only
|
||||
// a single subclase is provided
|
||||
if len(*clades) == 1 {
|
||||
keys := reflect.ValueOf(*clades).MapKeys()
|
||||
return iterator.IFilterOnSubcladeOf((*clades)[int(keys[0].Int())])
|
||||
}
|
||||
|
||||
new_iter := NewITaxonSet()
|
||||
|
||||
go func() {
|
||||
for iterator.Next() {
|
||||
tx := iterator.Get()
|
||||
if tx.IsBelongingSubclades(clades) {
|
||||
new_iter.source <- tx
|
||||
}
|
||||
}
|
||||
close(new_iter.source)
|
||||
}()
|
||||
|
||||
return new_iter
|
||||
}
|
||||
21
pkg/obitax/issuubcladeof.go
Normal file
21
pkg/obitax/issuubcladeof.go
Normal file
@@ -0,0 +1,21 @@
|
||||
package obitax
|
||||
|
||||
func (taxon *TaxNode) IsSubCladeOf(parent *TaxNode) bool {
|
||||
|
||||
for taxon.taxid != parent.taxid && taxon.parent != taxon.taxid {
|
||||
taxon = taxon.pparent
|
||||
}
|
||||
|
||||
return taxon.taxid == parent.taxid
|
||||
}
|
||||
|
||||
func (taxon *TaxNode) IsBelongingSubclades(clades *TaxonSet) bool {
|
||||
_, ok := (*clades)[taxon.taxid]
|
||||
|
||||
for !ok && taxon.parent != taxon.taxid {
|
||||
taxon = taxon.pparent
|
||||
_, ok = (*clades)[taxon.taxid]
|
||||
}
|
||||
|
||||
return ok
|
||||
}
|
||||
99
pkg/obitax/iterator.go
Normal file
99
pkg/obitax/iterator.go
Normal file
@@ -0,0 +1,99 @@
|
||||
package obitax
|
||||
|
||||
type ITaxonSet struct {
|
||||
source chan *TaxNode
|
||||
current *TaxNode
|
||||
finished bool
|
||||
p_finished *bool
|
||||
}
|
||||
|
||||
func NewITaxonSet() *ITaxonSet {
|
||||
i := ITaxonSet{make(chan *TaxNode), nil, false, nil}
|
||||
i.p_finished = &i.finished
|
||||
return &i
|
||||
}
|
||||
|
||||
func (set *TaxonSet) Iterator() *ITaxonSet {
|
||||
i := NewITaxonSet()
|
||||
|
||||
go func() {
|
||||
for _, t := range *set {
|
||||
i.source <- t
|
||||
}
|
||||
close(i.source)
|
||||
}()
|
||||
|
||||
return i
|
||||
}
|
||||
|
||||
func (set *TaxonSlice) Iterator() *ITaxonSet {
|
||||
i := NewITaxonSet()
|
||||
|
||||
go func() {
|
||||
for _, t := range *set {
|
||||
i.source <- t
|
||||
}
|
||||
close(i.source)
|
||||
}()
|
||||
|
||||
return i
|
||||
}
|
||||
|
||||
func (taxonmy *Taxonomy) iterator() *ITaxonSet {
|
||||
return taxonmy.nodes.Iterator()
|
||||
}
|
||||
|
||||
func (iterator *ITaxonSet) Next() bool {
|
||||
if *(iterator.p_finished) {
|
||||
return false
|
||||
}
|
||||
next, ok := (<-iterator.source)
|
||||
|
||||
if ok {
|
||||
iterator.current = next
|
||||
return true
|
||||
}
|
||||
|
||||
iterator.current = nil
|
||||
*iterator.p_finished = true
|
||||
return false
|
||||
}
|
||||
|
||||
// The 'Get' method returns the instance of *TaxNode
|
||||
// currently pointed by the iterator. You have to use the
|
||||
// 'Next' method to move to the next entry before calling
|
||||
// 'Get' to retreive the following instance.
|
||||
func (iterator *ITaxonSet) Get() *TaxNode {
|
||||
return iterator.current
|
||||
}
|
||||
|
||||
// Finished returns 'true' value if no more data is available
|
||||
// from the iterator.
|
||||
func (iterator *ITaxonSet) Finished() bool {
|
||||
return *iterator.p_finished
|
||||
}
|
||||
|
||||
func (iterator *ITaxonSet) Split() *ITaxonSet {
|
||||
new_iter := ITaxonSet{iterator.source, nil, false, iterator.p_finished}
|
||||
return &new_iter
|
||||
}
|
||||
|
||||
func (iterator *ITaxonSet) TaxonSet() *TaxonSet {
|
||||
set := make(TaxonSet)
|
||||
|
||||
for iterator.Next() {
|
||||
taxon := iterator.Get()
|
||||
set[taxon.taxid] = taxon
|
||||
}
|
||||
return &set
|
||||
}
|
||||
|
||||
func (iterator *ITaxonSet) TaxonSlice() *TaxonSlice {
|
||||
slice := make(TaxonSlice, 0)
|
||||
|
||||
for iterator.Next() {
|
||||
taxon := iterator.Get()
|
||||
slice = append(slice, taxon)
|
||||
}
|
||||
return &slice
|
||||
}
|
||||
36
pkg/obitax/path.go
Normal file
36
pkg/obitax/path.go
Normal file
@@ -0,0 +1,36 @@
|
||||
package obitax
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
func (taxon *TaxNode) Path() (*TaxonSlice, error) {
|
||||
|
||||
path := make(TaxonSlice, 0, 30)
|
||||
path = append(path, taxon)
|
||||
|
||||
for taxon != taxon.pparent {
|
||||
taxon = taxon.pparent
|
||||
|
||||
if taxon == nil {
|
||||
return nil, errors.New(fmt.Sprint("Taxonomy must be reindexed"))
|
||||
}
|
||||
|
||||
path = append(path, taxon)
|
||||
}
|
||||
|
||||
return &path, nil
|
||||
}
|
||||
|
||||
// Returns a TaxonSet listing the requested taxon and all
|
||||
// its ancestors in the taxonomy down to the root.
|
||||
func (taxonomy *Taxonomy) Path(taxid int) (*TaxonSlice, error) {
|
||||
taxon, err := taxonomy.Taxon(taxid)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return taxon.Path()
|
||||
}
|
||||
16
pkg/obitax/ranklist.go
Normal file
16
pkg/obitax/ranklist.go
Normal file
@@ -0,0 +1,16 @@
|
||||
package obitax
|
||||
|
||||
func (taxonomy *Taxonomy) RankList() []string {
|
||||
ranks := make([]string, 0, 30)
|
||||
mranks := make(map[string]bool)
|
||||
|
||||
for _, t := range *taxonomy.nodes {
|
||||
mranks[t.rank] = true
|
||||
}
|
||||
|
||||
for r := range mranks {
|
||||
ranks = append(ranks, r)
|
||||
}
|
||||
|
||||
return ranks
|
||||
}
|
||||
66
pkg/obitax/taxon.go
Normal file
66
pkg/obitax/taxon.go
Normal file
@@ -0,0 +1,66 @@
|
||||
package obitax
|
||||
|
||||
import (
|
||||
"regexp"
|
||||
)
|
||||
|
||||
type TaxNode struct {
|
||||
taxid int
|
||||
parent int
|
||||
pparent *TaxNode
|
||||
rank string
|
||||
scientificname *string
|
||||
alternatenames *map[string]*string
|
||||
}
|
||||
|
||||
func NewTaxNode(taxid int, parent int, rank string) *TaxNode {
|
||||
n := TaxNode{taxid, parent, nil, rank, nil, nil}
|
||||
return &n
|
||||
}
|
||||
|
||||
func (node *TaxNode) ScientificName() string {
|
||||
n := node.scientificname
|
||||
if n == nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
return *n
|
||||
}
|
||||
|
||||
func (node *TaxNode) Rank() string {
|
||||
return node.rank
|
||||
}
|
||||
|
||||
func (node *TaxNode) Taxid() int {
|
||||
return node.taxid
|
||||
}
|
||||
|
||||
func (node *TaxNode) Parent() *TaxNode {
|
||||
return node.pparent
|
||||
}
|
||||
|
||||
func (node *TaxNode) IsNameEqual(name string) bool {
|
||||
if *(node.scientificname) == name {
|
||||
return true
|
||||
}
|
||||
if node.alternatenames != nil {
|
||||
_, ok := (*node.alternatenames)[name]
|
||||
return ok
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (node *TaxNode) IsNameMatching(pattern *regexp.Regexp) bool {
|
||||
if pattern.MatchString(*(node.scientificname)) {
|
||||
return true
|
||||
}
|
||||
if node.alternatenames != nil {
|
||||
for n := range *node.alternatenames {
|
||||
if pattern.MatchString(n) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
135
pkg/obitax/taxonomy.go
Normal file
135
pkg/obitax/taxonomy.go
Normal file
@@ -0,0 +1,135 @@
|
||||
package obitax
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"log"
|
||||
)
|
||||
|
||||
type TaxName struct {
|
||||
name *string
|
||||
nameclass *string
|
||||
}
|
||||
|
||||
type Taxonomy struct {
|
||||
nodes *TaxonSet
|
||||
alias map[int]*TaxNode
|
||||
index map[string]*TaxonSet
|
||||
}
|
||||
|
||||
func NewTaxonomy() *Taxonomy {
|
||||
set := make(TaxonSet)
|
||||
taxonomy := Taxonomy{
|
||||
nodes: &set,
|
||||
alias: make(TaxonSet),
|
||||
index: make(map[string]*TaxonSet)}
|
||||
return &taxonomy
|
||||
}
|
||||
|
||||
func (taxonomy *Taxonomy) TaxonSet() *TaxonSet {
|
||||
return taxonomy.nodes
|
||||
}
|
||||
|
||||
func (taxonomy *Taxonomy) Alias() *map[int]*TaxNode {
|
||||
return &(taxonomy.alias)
|
||||
}
|
||||
|
||||
func (taxonomy *Taxonomy) Index() *map[string]*TaxonSet {
|
||||
return &(taxonomy.index)
|
||||
}
|
||||
|
||||
func (taxonomy *Taxonomy) Length() int {
|
||||
return len(*taxonomy.nodes)
|
||||
}
|
||||
|
||||
func (taxonomy *Taxonomy) Iterator() *ITaxonSet {
|
||||
return taxonomy.nodes.Iterator()
|
||||
}
|
||||
|
||||
func (taxonomy *Taxonomy) AddNewTaxa(taxid, parent int, rank string, replace bool, init bool) (*TaxNode, error) {
|
||||
if !replace {
|
||||
_, ok := (*taxonomy.nodes)[taxid]
|
||||
if ok {
|
||||
return nil, errors.New(fmt.Sprintf("Trying to add taxoon %d already present in the taxonomy", taxid))
|
||||
}
|
||||
}
|
||||
|
||||
n := NewTaxNode(taxid, parent, rank)
|
||||
(*taxonomy.nodes)[taxid] = n
|
||||
|
||||
return n, nil
|
||||
}
|
||||
|
||||
func (taxonomy *Taxonomy) Taxon(taxid int) (*TaxNode, error) {
|
||||
t, ok := (*taxonomy.nodes)[taxid]
|
||||
|
||||
if !ok {
|
||||
a, aok := taxonomy.alias[taxid]
|
||||
if !aok {
|
||||
return nil, errors.New(fmt.Sprintf("Taxid %d is not part of the taxonomy", taxid))
|
||||
}
|
||||
log.Printf("Taxid %d is deprecated and must be replaced by %d", taxid, a.taxid)
|
||||
t = a
|
||||
}
|
||||
return t, nil
|
||||
}
|
||||
|
||||
func (taxonomy *Taxonomy) AddNewName(taxid int, name, nameclass *string) error {
|
||||
node, node_err := taxonomy.Taxon(taxid)
|
||||
if node_err != nil {
|
||||
return node_err
|
||||
}
|
||||
|
||||
if *nameclass == "scientific name" {
|
||||
node.scientificname = name
|
||||
} else {
|
||||
names := node.alternatenames
|
||||
if names == nil {
|
||||
n := make(map[string]*string)
|
||||
names = &n
|
||||
node.alternatenames = names
|
||||
} else {
|
||||
(*names)[*name] = nameclass
|
||||
}
|
||||
}
|
||||
|
||||
i, ok := taxonomy.index[*name]
|
||||
if !ok {
|
||||
tnm := make(TaxonSet)
|
||||
i = &tnm
|
||||
taxonomy.index[*name] = i
|
||||
}
|
||||
(*i)[taxid] = node
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (taxonomy *Taxonomy) ReindexParent() error {
|
||||
var ok bool
|
||||
for _, taxon := range *taxonomy.nodes {
|
||||
taxon.pparent, ok = (*taxonomy.nodes)[taxon.parent]
|
||||
if !ok {
|
||||
return errors.New(fmt.Sprintf("Parent %d of taxon %d is not defined in taxonomy",
|
||||
taxon.taxid,
|
||||
taxon.parent))
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func MakeTaxName(name, nameclass *string) *TaxName {
|
||||
tn := TaxName{name, nameclass}
|
||||
return &tn
|
||||
}
|
||||
|
||||
func (taxonomy *Taxonomy) AddNewAlias(newtaxid, oldtaxid int) error {
|
||||
n, node_err := taxonomy.Taxon(newtaxid)
|
||||
if node_err != nil {
|
||||
return node_err
|
||||
}
|
||||
|
||||
taxonomy.alias[oldtaxid] = n
|
||||
|
||||
return nil
|
||||
}
|
||||
15
pkg/obitax/taxonset.go
Normal file
15
pkg/obitax/taxonset.go
Normal file
@@ -0,0 +1,15 @@
|
||||
package obitax
|
||||
|
||||
type TaxonSet map[int]*TaxNode
|
||||
|
||||
func (set *TaxonSet) Get(i int) *TaxNode {
|
||||
return (*set)[i]
|
||||
}
|
||||
|
||||
func (set *TaxonSet) Length() int {
|
||||
return len(*set)
|
||||
}
|
||||
|
||||
func (set *TaxonSet) Inserts(taxon *TaxNode) {
|
||||
(*set)[taxon.taxid] = taxon
|
||||
}
|
||||
11
pkg/obitax/taxonslice.go
Normal file
11
pkg/obitax/taxonslice.go
Normal file
@@ -0,0 +1,11 @@
|
||||
package obitax
|
||||
|
||||
type TaxonSlice []*TaxNode
|
||||
|
||||
func (set *TaxonSlice) Get(i int) *TaxNode {
|
||||
return (*set)[i]
|
||||
}
|
||||
|
||||
func (set *TaxonSlice) Length() int {
|
||||
return len(*set)
|
||||
}
|
||||
Reference in New Issue
Block a user