source: branches/nameserver/DIST/DI_clusters.cxx

Last change on this file was 17110, checked in by westram, 6 years ago
File size: 39.0 KB
Line 
1// =============================================================== //
2//                                                                 //
3//   File      : DI_clusters.cxx                                   //
4//   Purpose   : Detect clusters of homologous sequences in tree   //
5//                                                                 //
6//   Coded by Ralf Westram (coder@reallysoft.de) in October 2009   //
7//   Institute of Microbiology (Technical University Munich)       //
8//   http://www.arb-home.de/                                       //
9//                                                                 //
10// =============================================================== //
11
12#include "di_clusters.hxx"
13#include "di_clustertree.hxx"
14#include "di_foundclusters.hxx"
15#include "di_awars.hxx"
16
17#include <AP_seq_protein.hxx>
18#include <AP_seq_dna.hxx>
19
20#include <awt_sel_boxes.hxx>
21
22#include <aw_awars.hxx>
23#include <aw_msg.hxx>
24#include <arb_progress.h>
25#include <aw_root.hxx>
26
27#include <list>
28
29using namespace std;
30
31#define di_assert(cond) arb_assert(cond)
32
33// --------------
34//      awars
35
36#define AWAR_CLUSTER_PREFIX      AWAR_DIST_PREFIX "cluster/"
37#define AWAR_CLUSTER_PREFIX_TEMP "/tmp/" AWAR_DIST_PREFIX
38
39#define AWAR_CLUSTER_MAXDIST   AWAR_CLUSTER_PREFIX "maxdist"
40#define AWAR_CLUSTER_MINSIZE   AWAR_CLUSTER_PREFIX "minsize"
41#define AWAR_CLUSTER_AUTOMARK  AWAR_CLUSTER_PREFIX "automark"
42#define AWAR_CLUSTER_MARKREP   AWAR_CLUSTER_PREFIX "markrep"
43#define AWAR_CLUSTER_SELECTREP AWAR_CLUSTER_PREFIX "selrep"
44#define AWAR_CLUSTER_ORDER     AWAR_CLUSTER_PREFIX "order"
45
46#define AWAR_CLUSTER_GRP_PREFIX     AWAR_CLUSTER_PREFIX "group/"
47#define AWAR_CLUSTER_GRP_PREFIX_TMP "/tmp/" AWAR_CLUSTER_GRP_PREFIX
48
49#define AWAR_CLUSTER_GROUP_WHAT         AWAR_CLUSTER_GRP_PREFIX "all"
50#define AWAR_CLUSTER_GROUP_EXISTING     AWAR_CLUSTER_GRP_PREFIX "existing"
51#define AWAR_CLUSTER_GROUP_NOTFOUND     AWAR_CLUSTER_GRP_PREFIX "notfound"
52#define AWAR_CLUSTER_GROUP_IDENTITY     AWAR_CLUSTER_GRP_PREFIX "identity"
53#define AWAR_CLUSTER_GROUP_PREFIX       AWAR_CLUSTER_GRP_PREFIX "prefix"
54#define AWAR_CLUSTER_GROUP_PREFIX_MATCH AWAR_CLUSTER_GRP_PREFIX "prefix_match"
55#define AWAR_CLUSTER_GROUP_SUFFIX       AWAR_CLUSTER_GRP_PREFIX "suffix"
56#define AWAR_CLUSTER_GROUP_EXAMPLE      AWAR_CLUSTER_GRP_PREFIX_TMP "example"
57
58#define AWAR_CLUSTER_SELECTED      AWAR_CLUSTER_PREFIX_TEMP "selected" // ID of currently selected cluster (or zero)
59#define AWAR_CLUSTER_RESTORE_LABEL AWAR_CLUSTER_PREFIX_TEMP "rlabel" // label of restore button
60
61enum Group_What {
62    GROUP_SELECTED,
63    GROUP_LISTED,
64};
65
66enum Group_Action {
67    GROUP_CREATE,
68    GROUP_DELETE,
69};
70
71enum Group_NotFound {
72    NOTFOUND_ABORT,
73    NOTFOUND_WARN,
74    NOTFOUND_IGNORE,
75};
76
77enum Group_Existing {
78    EXISTING_GROUP_ABORT,
79    EXISTING_GROUP_SKIP,
80    EXISTING_GROUP_OVERWRITE,
81    EXISTING_GROUP_APPEND_ORG,
82};
83
84// ----------------------------------------
85
86enum AffectedClusters { ALL_CLUSTERS, SEL_CLUSTER };
87
88static ClustersData *global_data = NULp;
89
90// ------------------------
91
92static void di_forget_global_data(AW_window *) {
93    di_assert(global_data);
94    global_data->free();
95    // do not delete 'global_data' itself, it will be reused when window is opened again
96}
97
98// ------------------------
99//      Update contents
100
101static void update_cluster_sellist() {
102    global_data->update_cluster_selection_list();
103    // @@@ update result info line
104}
105static void update_restore_label(AW_window *aww) {
106    AW_root    *aw_root = aww->get_root();
107    AW_awar    *awar    = aw_root->awar(AWAR_CLUSTER_RESTORE_LABEL);
108    unsigned    size    = global_data->count(STORED_CLUSTERS);
109    const char *label   = size ? GBS_global_string("Stored: %u", size) : "None stored";
110
111    awar->write_string(label);
112}
113static void update_all(AW_window *aww) {
114    update_cluster_sellist();
115    update_restore_label(aww);
116}
117
118static void save_results_recursive(ClusterTree *subtree) {
119    if (subtree->get_state() == CS_IS_CLUSTER) {
120        global_data->add(new Cluster(subtree), SHOWN_CLUSTERS);
121    }
122    if (!subtree->is_leaf()) {
123        save_results_recursive(subtree->get_leftson());
124        save_results_recursive(subtree->get_rightson());
125    }
126}
127static void save_results(ClusterTreeRoot *tree) {
128    global_data->clear(SHOWN_CLUSTERS);
129    save_results_recursive(tree->get_root_node());
130    update_cluster_sellist();
131}
132
133static void calculate_clusters(AW_window *aww) {
134    GBDATA   *gb_main = global_data->get_gb_main();
135    GB_ERROR  error   = NULp;
136
137    arb_progress progress("Detecting clusters");
138
139    // calculate ClusterTree
140    ClusterTreeRoot *tree = NULp;
141    {
142        GB_transaction  ta(gb_main);
143        AW_root        *aw_root = aww->get_root();
144
145        {
146            char    *use     = aw_root->awar(AWAR_DIST_ALIGNMENT)->read_string();
147            AliView *aliview = global_data->weighted_filter.create_aliview(use, error);
148
149            if (aliview) {
150                AP_sequence *seq = GBT_is_alignment_protein(gb_main, use)
151                    ? (AP_sequence*)new AP_sequence_protein(aliview)
152                    : new AP_sequence_parsimony(aliview);
153
154                float    maxDistance    = aw_root->awar(AWAR_CLUSTER_MAXDIST)->read_float();
155                unsigned minClusterSize = aw_root->awar(AWAR_CLUSTER_MINSIZE)->read_int();
156
157                tree = new ClusterTreeRoot(aliview, seq, maxDistance/100, minClusterSize);
158
159                delete seq;
160            }
161            free(use);
162        }
163
164        if (!error) {
165            progress.subtitle("Loading tree");
166            {
167                char *tree_name = aw_root->awar(AWAR_DIST_TREE_CURR_NAME)->read_string();
168                error           = tree->loadFromDB(tree_name);
169                free(tree_name);
170            }
171
172            if (!error) error = tree->linkToDB(NULp, NULp);
173        }
174    }
175
176    if (!error) {
177        error = tree->find_clusters();
178        if (!error) save_results(tree);
179    }
180
181    delete tree;
182
183    if (error) aw_message(error);
184}
185
186DECLARE_CBTYPE_FVV_AND_BUILDERS(ClusterCallback, void, ClusterPtr); // generates makeClusterCallback
187
188inline bool is_cluster(ID id) {
189    // 0 is 'no cluster' and -1 is used for list header
190    return id>0;
191}
192
193static int with_affected_clusters_do(AW_root *aw_root, AffectedClusters affected, bool warn_if_none_affected, ClusterCallback cb) {
194    // returns number of affected clusters
195    int affCount = 0;
196    if (affected == SEL_CLUSTER) {
197        AW_awar *awar  = aw_root->awar(AWAR_CLUSTER_SELECTED);
198        ID       selID = awar->read_int();
199
200        if (is_cluster(selID)) {
201            ClusterPtr selCluster = global_data->clusterWithID(selID);
202            cl_assert(!selCluster.isNull());
203            cb(selCluster);
204            affCount++;
205        }
206        else if (warn_if_none_affected) {
207            aw_message("No cluster is selected");
208        }
209    }
210    else {
211        unsigned shown = global_data->count(SHOWN_CLUSTERS);
212        if (shown) {
213            ClusterIDs     clusters(global_data->get_clusterIDs(SHOWN_CLUSTERS)); // intended copy!
214            ClusterIDsIter cli_end = clusters.end();
215            for (ClusterIDsIter cli = clusters.begin(); cli != cli_end; ++cli) {
216                ClusterPtr cluster = global_data->clusterWithID(*cli);
217                cb(cluster);
218                affCount++;
219            }
220        }
221        else if (warn_if_none_affected) {
222            aw_message("There are no clusters in the list");
223        }
224    }
225    return affCount;
226}
227
228// -------------
229//      mark
230
231void Cluster::mark_all_members(ClusterMarkMode mmode) const {
232    DBItemSetIter sp_end = members.end();
233    for (DBItemSetIter sp = members.begin(); sp != sp_end; ++sp) {
234        bool is_rep = *sp == representative;
235        bool mark   = false;
236
237        switch (mmode) {
238            case CMM_ALL:         mark = true;    break;
239            case CMM_ONLY_REP:    mark = is_rep;  break;
240            case CMM_ALL_BUT_REP: mark = !is_rep; break;
241        }
242
243        if (mark) GB_write_flag(*sp, 1);
244    }
245}
246
247static void mark_cluster(ClusterPtr cluster, ClusterMarkMode markRep) {
248    cluster->mark_all_members(markRep);
249}
250static void select_representative(ClusterPtr cluster) {
251    GBDATA         *gb_species = cluster->get_representative();
252    GB_transaction  ta(gb_species);
253    const char     *name       = GBT_get_name(gb_species);
254    global_data->get_aw_root()->awar(AWAR_SPECIES_NAME)->write_string(name);
255}
256
257static void mark_clusters(AW_window *, AffectedClusters affected, bool warn_if_none_affected) {
258    AW_root *aw_root = global_data->get_aw_root();
259    GBDATA  *gb_main = global_data->get_gb_main();
260
261    GB_transaction  ta(gb_main);
262    ClusterMarkMode markRep = (ClusterMarkMode)aw_root->awar(AWAR_CLUSTER_MARKREP)->read_int();
263
264    GBT_mark_all(gb_main, 0); // unmark all
265    with_affected_clusters_do(aw_root, affected, warn_if_none_affected, makeClusterCallback(mark_cluster, markRep));
266    aw_root->awar(AWAR_TREE_REFRESH)->touch();
267}
268
269static void select_cluster_cb(AW_root *aw_root) {
270    GB_transaction ta(global_data->get_gb_main());
271
272    bool auto_mark = aw_root->awar(AWAR_CLUSTER_AUTOMARK)->read_int();
273    if (auto_mark) mark_clusters(NULp, SEL_CLUSTER, false);
274
275    bool selRep = aw_root->awar(AWAR_CLUSTER_SELECTREP)->read_int();
276    if (selRep) {
277        int selected = with_affected_clusters_do(aw_root, SEL_CLUSTER, false, makeClusterCallback(select_representative));
278        if (!selected) aw_root->awar(AWAR_SPECIES_NAME)->write_string(""); // deselect species
279    }
280}
281
282static void select_cluster(ID id) {
283    global_data->get_aw_root()->awar(AWAR_CLUSTER_SELECTED)->write_int(id);
284}
285
286
287// -------------------
288//      Sort order
289
290
291static void sort_order_changed_cb(AW_root *aw_root) {
292    ClusterOrder order = (ClusterOrder)aw_root->awar(AWAR_CLUSTER_ORDER)->read_int();
293    global_data->changeSortOrder(order);
294    update_cluster_sellist();
295}
296
297// --------------
298//      group
299
300class GroupTree;
301typedef map<string, GroupTree*> Species2Tip;
302
303struct GroupTreeRoot FINAL_TYPE : public ARB_seqtree_root {
304    GroupTreeRoot(AliView *aliView, AP_sequence *seqTempl, bool add_delete_callbacks);
305    ~GroupTreeRoot() OVERRIDE { predelete(); }
306    inline TreeNode *makeNode() const OVERRIDE;
307    inline void destroyNode(TreeNode *node) const OVERRIDE;
308};
309
310class GroupTree FINAL_TYPE : public ARB_countedTree {
311    unsigned leaf_count;   // total number of leafs in subtree
312    unsigned tagged_count; // tagged leafs
313
314    void update_tag_counters();
315    unsigned get_leaf_count() const OVERRIDE { return leaf_count; }
316protected:
317    ~GroupTree() OVERRIDE {}
318    friend class GroupTreeRoot;
319public:
320
321    explicit GroupTree(GroupTreeRoot *root)
322        : ARB_countedTree(root),
323          leaf_count(0),
324          tagged_count(0)
325    {}
326
327    // ARB_countedTree interface
328    void init_tree() OVERRIDE { update_leaf_counters(); }
329    // ARB_countedTree interface end
330
331    DEFINE_TREE_RELATIVES_ACCESSORS(GroupTree);
332
333    void map_species2tip(Species2Tip& mapping);
334
335    unsigned update_leaf_counters();
336
337    void tag_leaf() {
338        di_assert(is_leaf());
339        tagged_count = 1;
340        get_father()->update_tag_counters();
341    }
342    unsigned get_tagged_count() const { return tagged_count; }
343    void clear_tags();
344
345    float tagged_rate() const { return float(get_tagged_count())/get_leaf_count(); }
346};
347
348GroupTreeRoot::GroupTreeRoot(AliView *aliView, AP_sequence *seqTempl, bool add_delete_callbacks)
349    : ARB_seqtree_root(aliView, seqTempl, add_delete_callbacks)
350{}
351inline TreeNode *GroupTreeRoot::makeNode() const { return new GroupTree(const_cast<GroupTreeRoot*>(this)); }
352inline void GroupTreeRoot::destroyNode(TreeNode *node) const { delete DOWNCAST(GroupTree*,node); }
353
354
355unsigned GroupTree::update_leaf_counters() {
356    if (is_leaf()) leaf_count = 1;
357    else leaf_count = get_leftson()->update_leaf_counters() + get_rightson()->update_leaf_counters();
358    return leaf_count;
359}
360
361void GroupTree::clear_tags() {
362    if (!is_leaf() && tagged_count) {
363        get_leftson()->clear_tags();
364        get_rightson()->clear_tags();
365    }
366    tagged_count = 0;
367}
368
369void GroupTree::map_species2tip(Species2Tip& mapping) {
370    if (is_leaf()) {
371        if (name) mapping[name] = this;
372    }
373    else {
374        get_leftson()->map_species2tip(mapping);
375        get_rightson()->map_species2tip(mapping);
376    }
377}
378
379void GroupTree::update_tag_counters() {
380    di_assert(!is_leaf());
381    GroupTree *node = this;
382    while (node) {
383        node->tagged_count = node->get_leftson()->get_tagged_count() + node->get_rightson()->get_tagged_count();
384        node               = node->get_father();
385    }
386}
387
388struct GroupChanges {
389    unsigned created;
390    unsigned skipped;
391    unsigned overwritten;
392    unsigned deleted;
393    unsigned restored;
394
395    void clear() { created = skipped = overwritten = deleted = restored = 0; }
396    bool exist() const { return created||overwritten||deleted||restored; }
397
398    void show_message() {
399        string msg;
400
401        if (created)     msg += GBS_global_string("%u created  ",     created);
402        if (overwritten) msg += GBS_global_string("%u overwritten  ", overwritten);
403        if (skipped)     msg += GBS_global_string("%u skipped  ",     skipped);
404        if (deleted)     msg += GBS_global_string("%u deleted  ",     deleted);
405        if (restored)    msg += GBS_global_string("%u restored  ",    restored);
406
407        if (!msg.empty()) {
408            msg = string("Group changes: ")+msg;
409            aw_message(msg.c_str());
410        }
411    }
412   
413    GroupChanges() { clear(); }
414};
415
416
417// ---------------------
418//      GroupBuilder
419
420class GroupBuilder : virtual Noncopyable {
421    GBDATA         *gb_main;
422    string          tree_name;
423    GroupTreeRoot  *tree_root;
424    Group_Action    action;              // create or delete ?
425    Species2Tip     species2tip;         // map speciesName -> leaf
426    ARB_ERROR       error;
427    ClusterPtr      bad_cluster;         // error occurred here (is set)
428    Group_Existing  existing;
429    unsigned        existing_count;      // counts existing groups
430    Group_NotFound  notfound;
431    float           matchRatio;          // needed identity of subtree and cluster
432    float           maxDist;             // max. Distance used for calculation
433    string          cluster_prefix;      // prefix for cluster name
434    string          cluster_suffix_def;  // suffix-definition for cluster name
435    GroupChanges    changes;             // count tree modifications
436    bool            del_match_prefixes;  // only delete groups, where prefix matches
437
438    GroupTree *find_group_position(GroupTree *subtree, unsigned cluster_size);
439    float get_max_distance() const { return maxDist; }
440    void load_tree();
441
442    DEFINE_DOWNCAST_ACCESSORS(GroupTree, get_root_node, tree_root->get_root_node());
443
444    bool shall_delete_group(const char *name) const {
445        return !del_match_prefixes || matches_current_prefix(name);
446    }
447
448public:
449    GroupBuilder(GBDATA *gb_main_, Group_Action action_)
450        : gb_main(gb_main_),
451          tree_root(NULp),
452          action(action_),
453          error(NULp),
454          existing_count(0)
455    {
456        AW_root *awr = global_data->get_aw_root();
457
458        tree_name          = awr->awar(AWAR_DIST_TREE_CURR_NAME)->read_char_pntr();
459        existing           = (Group_Existing)awr->awar(AWAR_CLUSTER_GROUP_EXISTING)->read_int();
460        notfound           = (Group_NotFound)awr->awar(AWAR_CLUSTER_GROUP_NOTFOUND)->read_int();
461        del_match_prefixes = awr->awar(AWAR_CLUSTER_GROUP_PREFIX_MATCH)->read_int();
462        matchRatio         = awr->awar(AWAR_CLUSTER_GROUP_IDENTITY)->read_int()/100.0;
463        maxDist            = awr->awar(AWAR_CLUSTER_MAXDIST)->read_float();
464        cluster_prefix     = awr->awar(AWAR_CLUSTER_GROUP_PREFIX)->read_char_pntr();
465        cluster_suffix_def = awr->awar(AWAR_CLUSTER_GROUP_SUFFIX)->read_char_pntr();
466    }
467    ~GroupBuilder() { delete tree_root; }
468
469    ARB_ERROR get_error() const { return error; }
470    ClusterPtr get_bad_cluster() const { return bad_cluster; }
471
472    ARB_ERROR save_modified_tree();
473
474    GroupTree *find_best_matching_subtree(ClusterPtr cluster);
475    void update_group(ClusterPtr cluster); // create or delete group for cluster
476    string generate_group_name(ClusterPtr cluster, const GroupTree *group_node);
477
478    bool matches_current_prefix(const char *groupname) const {
479        return strstr(groupname, cluster_prefix.c_str()) == groupname;
480    }
481};
482
483void GroupBuilder::load_tree() {
484    di_assert(!tree_root);
485
486    tree_root = new GroupTreeRoot(new AliView(gb_main), NULp, false);
487    error     = tree_root->loadFromDB(tree_name.c_str());
488
489    if (error) {
490        delete tree_root;
491        tree_root = NULp;
492    }
493    else {
494        changes.clear();
495
496        GroupTree *tree = get_root_node();
497        tree->update_leaf_counters();
498        tree->map_species2tip(species2tip);
499    }
500}
501ARB_ERROR GroupBuilder::save_modified_tree() {
502    di_assert(!error);
503    if (changes.exist()) {
504        di_assert(tree_root);
505        error = tree_root->saveToDB();
506
507        AW_root *awr = global_data->get_aw_root();
508        awr->awar(AWAR_TREE_REFRESH)->touch();
509
510        if (!error) {
511            changes.show_message();
512            changes.clear();
513        }
514    }
515    return error;
516}
517
518GroupTree *GroupBuilder::find_group_position(GroupTree *subtree, unsigned cluster_size) {
519    // searches for best group in subtree matching the cluster
520
521    GroupTree *groupPos = NULp;
522    if (subtree->get_tagged_count() == cluster_size) {
523        groupPos                = find_group_position(subtree->get_leftson(), cluster_size);
524        if (!groupPos) groupPos = find_group_position(subtree->get_rightson(), cluster_size);
525
526        if (!groupPos) {                            // consider 'subtree'
527            if (subtree->tagged_rate() >= matchRatio) {
528                groupPos = subtree;
529            }
530        }
531    }
532    return groupPos;
533}
534
535class HasntCurrentClusterPrefix : public ARB_tree_predicate {
536    const GroupBuilder& builder;
537    bool selects(const ARB_seqtree& tree) const OVERRIDE {
538        const char *groupname        = tree.get_group_name();
539        bool        hasClusterPrefix = groupname && builder.matches_current_prefix(groupname);
540        return !hasClusterPrefix;
541    }
542public:
543    HasntCurrentClusterPrefix(const GroupBuilder& builder_) : builder(builder_) {}
544};
545
546string concatenate_name_parts(const list<string>& namepart) {
547    string concat;
548    for (list<string>::const_iterator p = namepart.begin(); p != namepart.end(); ++p) {
549        if (!p->empty()) concat += '_'+*p;
550    }
551    return concat.erase(0, 1);
552}
553
554class UseTreeRoot : public ARB_tree_predicate {
555    bool selects(const ARB_seqtree& tree) const OVERRIDE { return tree.is_root_node(); }
556};
557
558string GroupBuilder::generate_group_name(ClusterPtr cluster, const GroupTree *group_node) {
559    list<string> namepart;
560    namepart.push_back(cluster_prefix);
561
562    string  orgname_suffix;
563    if (existing == EXISTING_GROUP_APPEND_ORG) {
564        char *old_name = group_node->name;
565        if (old_name) {
566            char *original = originalGroupName(old_name);
567            if (!original && !matches_current_prefix(old_name)) {
568                original = ARB_strdup(old_name); // use existing name as original name
569            }
570            if (original) {
571                orgname_suffix = string(" {was:")+original+"}";
572                free(original);
573            }
574        }
575    }
576
577    string text;
578    for (int i = 0; cluster_suffix_def[i]; ++i) {
579        if (cluster_suffix_def[i] == '%') {
580            ++i;
581            if (cluster_suffix_def[i] == '%') {
582                text += '%';
583            }
584            else {
585                string expanded;
586                switch(cluster_suffix_def[i]) {
587                    case 'p': expanded = cluster->get_upgroup_info(group_node, HasntCurrentClusterPrefix(*this)); break;
588                    case 'P': expanded = cluster->get_upgroup_info(group_node, UseAnyTree()); break;
589                    case 't': expanded = cluster->get_upgroup_info(group_node, UseTreeRoot()); break;
590                    case 'd': expanded = GBS_global_string("~%.3f", cluster->get_mean_distance()); break;
591                    case 'D': expanded = GBS_global_string("<=%.1f%%", get_max_distance()); break;
592                    case 'e': expanded = group_node->name ? group_node->name : ""; break;
593                    case 'o': {
594                        int matchRate = int(group_node->tagged_rate()*100+0.5);
595                        if (matchRate<100) expanded = GBS_global_string("%i%%_of", matchRate);
596                        break;
597                    }
598                    default:
599                        text += '%';
600                        text += cluster_suffix_def[i];
601                        break;
602                }
603
604                if (!expanded.empty()) {
605                    namepart.push_back(text);
606                    text = "";
607                    namepart.push_back(expanded);
608                }
609            }
610        }
611        else {
612            text += cluster_suffix_def[i];
613        }
614    }
615    namepart.push_back(text);
616
617    return concatenate_name_parts(namepart)+orgname_suffix;
618}
619
620GroupTree *GroupBuilder::find_best_matching_subtree(ClusterPtr cluster) {
621    GroupTree *group_node = NULp;
622    if (!error) {
623        if (!tree_root) load_tree();
624        if (!error) {
625            const DBItemSet& members = cluster->get_members();
626
627            // mark cluster members in tree
628            {
629                GB_transaction ta(gb_main);
630                DBItemSetIter sp_end = members.end();
631                for (DBItemSetIter sp = members.begin(); sp != sp_end && !error; ++sp) {
632                    const char *name = GBT_get_name(*sp);
633                    di_assert(name);
634                    if (name) {
635                        Species2Tip::const_iterator found = species2tip.find(name);
636                        if (found == species2tip.end()) {
637                            error = GBS_global_string("Species '%s' is not in '%s'", name, tree_name.c_str());
638                        }
639                        else {
640                            GroupTree *leaf = found->second;
641                            leaf->tag_leaf();
642                        }
643                    }
644                }
645            }
646
647            if (!error) {
648                // top-down search for best matching node
649                group_node = find_group_position(get_root_node(), cluster->get_member_count());
650            }
651        }
652    }
653    return group_node;
654}
655
656void GroupBuilder::update_group(ClusterPtr cluster) {
657    if (!error) {
658        GroupTree *group_node = find_best_matching_subtree(cluster);
659        if (!error) {
660            if (!group_node) { // no matching subtree found
661                switch (notfound) {
662                    case NOTFOUND_WARN:
663                    case NOTFOUND_ABORT: {
664                        const char *msg = GBS_global_string("Could not find matching subtree for cluster '%s'", cluster->get_list_display(NULp));
665                        if (notfound == NOTFOUND_ABORT) error = msg;
666                        else aw_message(msg);
667                        break;
668                    }
669                    case NOTFOUND_IGNORE: break; // silently ignore
670                }
671            }
672            else { // found subtree for group
673                switch (action) {
674                    case GROUP_CREATE: {
675                        char *old_name = group_node->name;
676
677                        if (old_name && existing == EXISTING_GROUP_ABORT) {
678                            error = GBS_global_string("Existing group '%s' is in the way", old_name);
679                        }
680                        else {
681                            if (old_name && existing == EXISTING_GROUP_SKIP) {
682                                changes.skipped++;
683                            }
684                            else {
685                                string new_name = generate_group_name(cluster, group_node);
686
687                                if (old_name) changes.overwritten++; else changes.created++;
688
689                                free(old_name);
690                                group_node->name = ARB_strdup(new_name.c_str());
691
692                                // @@@ DRY that.. it's spread everywhere through libs :(
693                                if (!group_node->gb_node) {
694                                    GBDATA *gb_tree = group_node->get_tree_root()->get_gb_tree();
695                                    GB_transaction ta(gb_tree);
696                                    GBDATA *gb_node = GB_create_container(gb_tree, "node");
697                                    if (!gb_node) {
698                                        error = GB_await_error();
699                                    }
700                                    else {
701                                        error = GBT_write_int(gb_node, "id", 0);
702                                    }
703
704                                    if (!error) {
705                                        group_node->gb_node = gb_node;
706                                    }
707                                }
708                                if (group_node->gb_node && !error) {
709                                    GB_transaction ta(group_node->gb_node);
710                                    error = GBT_write_name_to_groupData(group_node->gb_node, true, group_node->name, true);
711                                    // @@@ if INT-field "keeled" exists -> set to 0
712                                }
713
714                                cluster->update_description(group_node); // change list display
715                            }
716                        }
717                        break;
718                    }
719                    case GROUP_DELETE: {
720                        if (group_node->name && shall_delete_group(group_node->name)) {
721                            char *original = originalGroupName(group_node->name);
722
723                            if (original) {
724                                freeset(group_node->name, original); // restore original name
725                                if (group_node->gb_node) {
726                                    error = GBT_write_name_to_groupData(group_node->gb_node, true, group_node->name, true);
727                                    // @@@ original keeled state is not restored here
728                                }
729                                changes.restored++;
730                            }
731                            else {
732                                freenull(group_node->name);
733                                group_node->gb_node = NULp; // forget ref to group data (@@@ need to delete group data from DB?)
734                                changes.deleted++;
735                            }
736
737                            cluster->update_description(group_node); // change list display
738                        }
739                        break;
740                    }
741                }
742            }
743        }
744
745        if (error) bad_cluster = cluster;
746        get_root_node()->clear_tags();
747    }
748}
749
750static void update_example(AW_root *aw_root) {
751    ID     selID = aw_root->awar(AWAR_CLUSTER_SELECTED)->read_int();
752    string value;
753
754    if (is_cluster(selID)) {
755        ClusterPtr selCluster = global_data->clusterWithID(selID);
756        cl_assert(!selCluster.isNull());
757
758        GroupBuilder  builder(global_data->get_gb_main(), GROUP_CREATE);
759        GroupTree    *group_node = builder.find_best_matching_subtree(selCluster);
760
761        GB_ERROR error = builder.get_error().deliver();
762       
763        if (error)           value = GBS_global_string("<error: %s>", error);
764        else if (group_node) value = builder.generate_group_name(selCluster, group_node);
765        else                 value = "<no matching subtree found>";
766    }
767    else value = "<no cluster selected>";
768    aw_root->awar(AWAR_CLUSTER_GROUP_EXAMPLE)->write_string(value.c_str());
769}
770
771static void update_cluster_group(ClusterPtr cluster, GroupBuilder *groupBuilder) {
772    if (!groupBuilder->get_error()) {
773        groupBuilder->update_group(cluster);
774    }
775}
776
777static void accept_proposed_names(ClusterPtr cluster, bool accept) {
778    cluster->accept_proposed(accept);
779}
780
781static void group_clusters(AW_window *, Group_Action action) {
782    AW_root          *aw_root  = global_data->get_aw_root();
783    Group_What        what     = (Group_What)aw_root->awar(AWAR_CLUSTER_GROUP_WHAT)->read_int();
784    AffectedClusters  affected = what == GROUP_LISTED ? ALL_CLUSTERS : SEL_CLUSTER;
785
786    GroupBuilder groupBuilder(global_data->get_gb_main(), action);
787
788    GB_transaction ta(global_data->get_gb_main());
789    with_affected_clusters_do(aw_root, affected, true, makeClusterCallback(update_cluster_group, &groupBuilder));
790
791    ARB_ERROR error = groupBuilder.get_error();
792    if (error) {
793        ClusterPtr bad = groupBuilder.get_bad_cluster();
794        if (!bad.isNull()) {
795            select_cluster(bad->get_ID());
796            aw_message("Problematic cluster has been highlighted");
797        }
798    }
799    else {
800        error = groupBuilder.save_modified_tree();
801    }
802
803    error = ta.close(error);
804
805    bool accept = !error;
806    aw_message_if(error);
807    // careful! the following code will invalidate error, so don't use below
808
809    with_affected_clusters_do(aw_root, affected, false, makeClusterCallback(accept_proposed_names, accept)); // just affects display
810    global_data->update_cluster_selection_list();
811}
812
813static void popup_group_clusters_window(AW_window *aw_clusterList) {
814    static AW_window_simple *aws = NULp;
815
816    if (!aws) {
817        AW_root *aw_root = aw_clusterList->get_root();
818
819        aws = new AW_window_simple;
820        aws->init(aw_root, "cluster_groups", "Cluster groups");
821
822        aws->auto_space(10, 10);
823
824        aws->callback(AW_POPDOWN);
825        aws->create_button("CLOSE", "CLOSE", "C");
826
827        aws->callback(makeHelpCallback("cluster_group.hlp"));
828        aws->create_button("HELP", "HELP");
829
830        aws->at_newline();
831
832        aws->label("For");
833        aws->create_option_menu(AWAR_CLUSTER_GROUP_WHAT, true);
834        aws->insert_option        ("selected cluster", "s", GROUP_SELECTED);
835        aws->insert_default_option("listed clusters",  "l", GROUP_LISTED);
836        aws->update_option_menu();
837
838        aws->at_newline();
839
840        aws->label("with a min. cluster/subtree identity (%) of");
841        aws->create_input_field(AWAR_CLUSTER_GROUP_IDENTITY, 4);
842
843        aws->at_newline();
844
845        aws->label("-> if no matching subtree found");
846        aws->create_option_menu(AWAR_CLUSTER_GROUP_NOTFOUND, true);
847        aws->insert_default_option("abort",          "a", NOTFOUND_ABORT);
848        aws->insert_option        ("warn",           "w", NOTFOUND_WARN);
849        aws->insert_option        ("ignore",         "i", NOTFOUND_IGNORE);
850        aws->update_option_menu();
851
852        aws->at_newline();
853
854        aws->callback(makeWindowCallback(group_clusters, GROUP_CREATE));
855        aws->create_autosize_button("CREATE_GROUPS", "create groups!");
856
857        aws->label("If group exists");
858        aws->create_option_menu(AWAR_CLUSTER_GROUP_EXISTING, true);
859        aws->insert_default_option("abort",                "a", EXISTING_GROUP_ABORT);
860        aws->insert_option        ("skip",                 "s", EXISTING_GROUP_SKIP);
861        aws->insert_option        ("overwrite (caution!)", "o", EXISTING_GROUP_OVERWRITE);
862        aws->insert_option        ("append original",      "p", EXISTING_GROUP_APPEND_ORG);
863        aws->update_option_menu();
864
865        aws->at_newline();
866
867        aws->callback(makeWindowCallback(group_clusters, GROUP_DELETE));
868        aws->create_autosize_button("DELETE_GROUPS", "delete groups!");
869
870        aws->create_text_toggle(AWAR_CLUSTER_GROUP_PREFIX_MATCH, "(all)", "(where prefix matches)", 30);
871
872        aws->at_newline(); aws->label("Name prefix:"); aws->create_input_field(AWAR_CLUSTER_GROUP_PREFIX, 20);
873        aws->at_newline(); aws->label("Name suffix:"); aws->create_input_field(AWAR_CLUSTER_GROUP_SUFFIX, 20);
874
875        aws->at_newline();
876        aws->button_length(60);
877        aws->label("=>"); 
878        aws->create_button(NULp, AWAR_CLUSTER_GROUP_EXAMPLE);
879
880        aws->window_fit();
881    }
882
883    aws->activate();
884}
885
886// ---------------
887//      delete
888
889static void delete_selected_cluster(ClusterPtr cluster) {
890    int pos    = global_data->get_pos(cluster, SHOWN_CLUSTERS);
891    int nextId = global_data->idAtPos(pos+1, SHOWN_CLUSTERS);
892    select_cluster(nextId);
893    global_data->remove(cluster, SHOWN_CLUSTERS);
894}
895static void delete_clusters(AW_window *aww, AffectedClusters affected) {
896    switch (affected) {
897        case SEL_CLUSTER:
898            with_affected_clusters_do(aww->get_root(), affected, true, makeClusterCallback(delete_selected_cluster));
899            break;
900        case ALL_CLUSTERS:
901            select_cluster(0);
902            global_data->clear(SHOWN_CLUSTERS);
903            break;
904    }
905
906    update_cluster_sellist();
907}
908
909// ----------------------
910//      store/restore
911
912static void store_selected_cluster(ClusterPtr cluster) {
913    int pos    = global_data->get_pos(cluster, SHOWN_CLUSTERS);
914    int nextId = global_data->idAtPos(pos+1, SHOWN_CLUSTERS);
915
916    select_cluster(nextId);
917    global_data->store(cluster->get_ID());
918}
919static void store_clusters(AW_window *aww, AffectedClusters affected) {
920    switch (affected) {
921        case SEL_CLUSTER:
922            with_affected_clusters_do(aww->get_root(), affected, true, makeClusterCallback(store_selected_cluster));
923            break;
924        case ALL_CLUSTERS:
925            select_cluster(0);
926            global_data->store_all();
927            break;
928    }
929
930    update_all(aww);
931}
932
933
934static void restore_clusters(AW_window *aww) {
935    global_data->restore_all();
936    update_all(aww);
937}
938static void swap_clusters(AW_window *aww) {
939    global_data->swap_all();
940    select_cluster(0);
941    update_all(aww);
942}
943
944// ---------------------------------
945//      cluster detection window
946
947AW_window *DI_create_cluster_detection_window(AW_root *aw_root, WeightedFilter *weightedFilter) {
948    static AW_window_simple *aws = NULp;
949    if (!aws) {
950        cl_assert(!global_data);
951        global_data = new ClustersData(*weightedFilter);
952
953        aws = new AW_window_simple;
954        aws->init(aw_root, "DETECT_CLUSTERS", "Detect clusters in tree");
955        aws->load_xfig("di_clusters.fig");
956
957        aws->on_hide(di_forget_global_data);
958
959        // -------------------
960        //      upper area
961
962        aws->at("close");
963        aws->callback(AW_POPDOWN);
964        aws->create_button("CLOSE", "CLOSE");
965
966        aws->at("help");
967        aws->callback(makeHelpCallback("di_clusters.hlp"));
968        aws->create_button("HELP", "HELP");
969
970        aws->at("max_dist");
971        aws->d_callback(makeWindowCallback(calculate_clusters));
972        aws->create_input_field(AWAR_CLUSTER_MAXDIST, 12);
973
974        aws->at("min_size");
975        aws->d_callback(makeWindowCallback(calculate_clusters));
976        aws->create_input_field(AWAR_CLUSTER_MINSIZE, 5);
977
978        aws->at("calculate");
979        aws->callback(calculate_clusters);
980        aws->create_autosize_button("CALC", "Detect clusters");
981
982        aws->button_length(20);
983        aws->at("tree_name");
984        aws->create_button(NULp, AWAR_DIST_TREE_CURR_NAME);
985
986        // -------------------
987        //      lower area
988
989        aws->button_length(10);
990
991        // column 1
992
993        aws->at("select_rep"); aws->create_toggle(AWAR_CLUSTER_SELECTREP);
994        aws->at("mark"); aws->callback(makeWindowCallback(mark_clusters, SEL_CLUSTER, true)); aws->create_button("MARK", "Mark");
995        aws->at("auto_mark"); aws->create_toggle(AWAR_CLUSTER_AUTOMARK);
996
997        aws->at("mark_what");
998        aws->create_option_menu(AWAR_CLUSTER_MARKREP, true);
999        aws->insert_default_option("cluster w/o repr.",   "d", CMM_ALL_BUT_REP);
1000        aws->insert_option        ("whole cluster",       "b", CMM_ALL);
1001        aws->insert_option        ("only representative", "s", CMM_ONLY_REP);
1002        aws->update_option_menu();
1003
1004        aws->at("mark_all"); aws->callback(makeWindowCallback(mark_clusters, ALL_CLUSTERS, true)); aws->create_button("MARKALL", "Mark all");
1005
1006        // column 2
1007
1008        aws->button_length(18);
1009
1010        aws->at("group"); aws->callback(popup_group_clusters_window); aws->create_button("GROUP", "Cluster groups..");
1011
1012        aws->at("sort");
1013        aws->create_option_menu(AWAR_CLUSTER_ORDER, true);
1014        aws->insert_default_option("by mean distance",  "d", SORT_BY_MEANDIST);
1015        aws->insert_option        ("by min bases used", "b", SORT_BY_MIN_BASES);
1016        aws->insert_option        ("by size",           "s", SORT_BY_CLUSTERSIZE);
1017        aws->insert_option        ("by tree position",  "p", SORT_BY_TREEPOSITION);
1018        aws->insert_option        ("by min distance",   "i", SORT_BY_MIN_DIST);
1019        aws->insert_option        ("by max distance",   "x", SORT_BY_MAX_DIST);
1020        aws->insert_option        ("reverse",           "r", SORT_REVERSE);
1021        aws->update_option_menu();
1022
1023        // store/restore
1024
1025        aws->at("store_all"); aws->callback(makeWindowCallback(store_clusters, ALL_CLUSTERS)); aws->create_button("STOREALL", "Store all");
1026        aws->at("store");     aws->callback(makeWindowCallback(store_clusters, SEL_CLUSTER));  aws->create_button("STORESEL", "Store selected");
1027        aws->at("restore");   aws->callback(restore_clusters);                                 aws->create_button("RESTORE",  AWAR_CLUSTER_RESTORE_LABEL);
1028        aws->at("swap");      aws->callback(swap_clusters);                                    aws->create_button("Swap",     "Swap stored");
1029
1030        // column 4
1031
1032        aws->at("clear");  aws->callback(makeWindowCallback(delete_clusters, ALL_CLUSTERS)); aws->create_button("CLEAR", "Clear list");
1033        aws->at("delete"); aws->callback(makeWindowCallback(delete_clusters, SEL_CLUSTER));  aws->create_button("DEL",   "Delete selected");
1034
1035        // --------------------
1036        //      clusterlist
1037
1038        aws->at("cluster_list");
1039        global_data->clusterList = aws->create_selection_list(AWAR_CLUSTER_SELECTED, true);
1040        update_cluster_sellist();
1041
1042        aw_root->awar(AWAR_CLUSTER_SELECTED)->add_callback(select_cluster_cb);
1043        aw_root->awar(AWAR_CLUSTER_ORDER)->add_callback(sort_order_changed_cb);
1044        sort_order_changed_cb(aw_root);
1045    }
1046
1047    return aws;
1048}
1049
1050void DI_create_cluster_awars(AW_root *aw_root, AW_default def, AW_default db) {
1051    aw_root->awar_float(AWAR_CLUSTER_MAXDIST,   3.0,             def)->set_minmax(0.0, 100.0);
1052    aw_root->awar_int  (AWAR_CLUSTER_MINSIZE,   7,               def)->set_minmax(2, INT_MAX);
1053    aw_root->awar_int  (AWAR_CLUSTER_AUTOMARK,  1,               def);
1054    aw_root->awar_int  (AWAR_CLUSTER_MARKREP,   CMM_ALL_BUT_REP, def);
1055    aw_root->awar_int  (AWAR_CLUSTER_SELECTREP, 1,               def);
1056
1057    aw_root->awar_int   (AWAR_CLUSTER_ORDER,         SORT_BY_MEANDIST, def);
1058    aw_root->awar_string(AWAR_CLUSTER_RESTORE_LABEL, "None stored",    def);
1059
1060    aw_root->awar_int(AWAR_CLUSTER_GROUP_WHAT,     GROUP_LISTED,   def);
1061    aw_root->awar_int(AWAR_CLUSTER_GROUP_NOTFOUND, NOTFOUND_ABORT, def);
1062
1063    aw_root->awar_int   (AWAR_CLUSTER_GROUP_IDENTITY,     100, def)->set_minmax(1, 100);
1064    aw_root->awar_int   (AWAR_CLUSTER_GROUP_PREFIX_MATCH, 1,   def);
1065    aw_root->awar_string(AWAR_CLUSTER_GROUP_EXAMPLE,      "",  def);
1066
1067    aw_root->awar_int   (AWAR_CLUSTER_SELECTED,       0,                    def)->add_callback(update_example);
1068    aw_root->awar_int   (AWAR_CLUSTER_GROUP_EXISTING, EXISTING_GROUP_ABORT, def)->add_callback(update_example);
1069    aw_root->awar_string(AWAR_CLUSTER_GROUP_PREFIX,   "cluster",            def)->add_callback(update_example);
1070    aw_root->awar_string(AWAR_CLUSTER_GROUP_SUFFIX,   "%o%p",               def)->add_callback(update_example);
1071
1072    aw_root->awar_int(AWAR_TREE_REFRESH, 0, db);
1073
1074    update_example(aw_root);
1075}
1076
Note: See TracBrowser for help on using the repository browser.