NCBI C++ ToolKit
phylo_selection_set.cpp
Go to the documentation of this file.

Go to the SVN repository for this file.

1 /* $Id: phylo_selection_set.cpp 36928 2016-11-15 14:28:10Z falkrb $
2 * ===========================================================================
3 *
4 * PUBLIC DOMAIN NOTICE
5 * National Center for Biotechnology Information
6 *
7 * This software/database is a "United States Government Work" under the
8 * terms of the United States Copyright Act. It was written as part of
9 * the author's official duties as a United States Government employee and
10 * thus cannot be copyrighted. This software/database is freely available
11 * to the public for use. The National Library of Medicine and the U.S.
12 * Government have not placed any restriction on its use or reproduction.
13 *
14 * Although all reasonable efforts have been taken to ensure the accuracy
15 * and reliability of the software and data, the NLM and the U.S.
16 * Government do not and cannot warrant the performance or results that
17 * may be obtained by using this software or data. The NLM and the U.S.
18 * Government disclaim all warranties, express or implied, including
19 * warranties of performance, merchantability or fitness for any particular
20 * purpose.
21 *
22 * Please cite the author in any work or product based on this material.
23 *
24 * ===========================================================================
25 *
26 * Authors: Bob Falk
27 *
28 * File Description:
29 *
30 */
31 
32 #include <ncbi_pch.hpp>
34 
37 
41 
42 
44 
45 using namespace objects;
46 
48 {
49  // We don't care about m_ClusterID and selected status for purposes
50  // of equality
51  return (m_SelectedIDs == rhs.m_SelectedIDs &&
52  m_SelectionName == rhs.m_SelectionName &&
53  m_SelectionColor == rhs.m_SelectionColor);
54 }
55 
56 
58 {
59  std::sort(m_SelectedIDs.begin(), m_SelectedIDs.end());
60  m_SelectedIDs.erase(std::unique(m_SelectedIDs.begin(), m_SelectedIDs.end()),
61  m_SelectedIDs.end());
62 }
63 
65 {
66  if (sel_set->HasField("selection-name") &&
67  sel_set->GetField("selection-name").GetData().IsStr()) {
68  m_SelectionName = sel_set->GetField("selection-name").GetData().GetStr();
69  }
70  if (sel_set->HasField("color-red") && sel_set->HasField("color-green") && sel_set->HasField("color-blue") &&
71  sel_set->GetField("color-red").GetData().IsReal() &&
72  sel_set->GetField("color-green").GetData().IsReal() &&
73  sel_set->GetField("color-blue").GetData().IsReal()) {
74  m_SelectionColor.Set(float(sel_set->GetField("color-red").GetData().GetReal()),
75  float(sel_set->GetField("color-green").GetData().GetReal()),
76  float(sel_set->GetField("color-blue").GetData().GetReal()));
77  }
78  if (sel_set->HasField("selected") &&
79  sel_set->GetField("selected").GetData().IsBool()) {
80  m_Selected = sel_set->GetField("selected").GetData().GetBool();
81  }
82  if (sel_set->HasField("cluster-id") &&
83  sel_set->GetField("cluster-id").GetData().IsInt()) {
84  m_ClusterID = sel_set->GetField("cluster-id").GetData().GetInt();
85  }
86  if (sel_set->HasField("selection-ids") &&
87  sel_set->GetField("selection-ids").GetData().IsInts()) {
88  vector<int> node_ids = sel_set->GetField("selection-ids").GetData().GetInts();
89 
90  m_SelectedIDs.clear();
91  for (size_t i = 0; i<node_ids.size(); ++i) {
92  m_SelectedIDs.push_back(node_ids[i]);
93  }
94  }
95 }
96 
98 {
99  CRef<CUser_object> uo_sel_set;
100  uo_sel_set.Reset(new CUser_object());
101 
102  CRef<CObject_id> uo_id;
103  uo_id.Reset(new CObject_id());
104  uo_id->SetStr("selection-set");
105  uo_sel_set->SetType(*uo_id);
106 
107  uo_sel_set->AddField("selection-name", GetName());
108  uo_sel_set->AddField("color-red", GetColor().GetRed());
109  uo_sel_set->AddField("color-green", GetColor().GetGreen());
110  uo_sel_set->AddField("color-blue", GetColor().GetBlue());
111  uo_sel_set->AddField("selected", GetSelected());
112  uo_sel_set->AddField("cluster-id", GetClusterID());
113 
114  // need ints, not size_t's:
115  vector<int> selected;
116  for (size_t j = 0; j<GetSelectionSet().size(); ++j)
117  selected.push_back(int(GetSelectionSet()[j]));
118 
119  uo_sel_set->AddField("selection-ids", selected);
120 
121  return uo_sel_set;
122 }
123 
125 {
126  if (m_SelectionSets.size() != rhs.m_SelectionSets.size())
127  return false;
128 
129  for (size_t i = 0; i < m_SelectionSets.size(); ++i) {
130  if (!(m_SelectionSets[i] == rhs.m_SelectionSets[i]))
131  return false;
132  }
133 
134  return true;
135 }
136 
139 {
140  if (uo.HasField("selection-sets") &&
141  uo.GetField("selection-sets").GetData().IsObjects()) {
142  vector< CRef< CUser_object > > sel_sets = uo.GetField("selection-sets").GetData().GetObjects();
143 
144  m_SelectionSets.resize(sel_sets.size());
145  for (size_t i = 0; i < sel_sets.size(); ++i) {
146  m_SelectionSets[i].InitFromUserObject(sel_sets[i]);
147  }
148  // sync with current model immediately after loading
149  x_SyncSelectionSets(tree_model);
150  // set selection
151  SetSelectionSetProperty(tree_model);
152  }
153 }
154 
156 {
157  if (uo.HasField("selection-sets")) {
158  CUser_object::TData::iterator it = uo.SetData().begin();
159  while (it != uo.SetData().end()) {
160  if ((*it)->IsSetLabel() && (*it)->GetLabel().IsStr() &&
161  (*it)->GetLabel().GetStr() == "selection-sets") {
162  uo.SetData().erase(it);
163  break;
164  }
165  ++it;
166  }
167  }
168 
169  if (m_SelectionSets.size() > 0) {
170  // create a cuserobject with appropriate fields for each
171  // entry, and add that under the name 'selection-sets' to uo:
172  vector< CRef<CUser_object> > uo_sets;
173 
174  for (size_t i = 0; i < m_SelectionSets.size(); ++i) {
175  CRef<CUser_object> uo_sel_set = m_SelectionSets[i].SaveToUserObject();
176 
177  uo_sets.push_back(uo_sel_set);
178  }
179  uo.AddField("selection-sets", uo_sets);
180  }
181 }
182 
184 {
185  for (auto& sel_set : m_SelectionSets) {
186  sel_set.RemoveDuplicateIDs();
187  }
188 }
189 
191 {
192  map<int, size_t> cluster_id_to_selection_map;
193 
194  for (size_t i = 0; i<m_SelectionSets.size(); ++i) {
195  if (m_SelectionSets[i].GetSelected()) {
196  int id = m_SelectionSets[i].GetClusterID();
197  cluster_id_to_selection_map[id] = i;
198  }
199  }
200 
201  return cluster_id_to_selection_map;
202 }
203 
204 
206 {
207  // First clear any existing selection set cluster ids.
208  size_t num_nodes = tree_model->GetSize();
209  TBioTreeFeatureId cluster_id = tree_model->GetFeatureDict().Register("cluster-id");
210  TBioTreeFeatureId sel_feat_id = tree_model->GetFeatureDict().Register("$SEL_CLUSTERS");
211  TBioTreeFeatureId marker_feat_id = tree_model->GetFeatureDict().Register("marker");
212 
213  // Clear cluseter info before re-adding it following loop
214  //------------------------------------------------------------------------------------
215  if (sel_feat_id != (TBioTreeFeatureId)-1) {
216  for (size_t i = 0; i<num_nodes; ++i) {
217  CPhyloTreeNode& n = tree_model->GetNode(i);
218 
219  // clear all selection cluster info - that will be re-added below if there is any
220  if (n.GetValue().HasSelClusters())
221  n.GetValue().GetSelClusters().clear();
222  n.GetValue().GetBioTreeFeatureList().RemoveFeature(sel_feat_id);
223 
224  // reset cluster id from feature list, if available
225  string cid = n.GetValue().GetBioTreeFeatureList().GetFeatureValue(cluster_id);
226  int cluster_id = -1;
227  if (cid != "") {
228  int sel_id = NStr::StringToInt(cid, NStr::fConvErr_NoThrow);
229  if (sel_id || errno == 0)
230  cluster_id = sel_id;
231  }
232  n.GetValue().SetClusterID(cluster_id);
233  n.GetValue().SetClusterColorIdx(-1);
234 
235  // Reset node marker from feature (if available) since it can also be overridden by
236  // selection clusters
237  if (n.GetValue().HasNodeMarker())
238  n.GetValue().GetMarkerColors().clear();
239  const string& marker = n.GetValue().GetBioTreeFeatureList().GetFeatureValue(marker_feat_id);
240  try {
241  if (!marker.empty()) {
242  n.GetValue().SetMarkerColors(marker);
243  }
244  }
245  catch (std::exception&){}
246  }
247  }
248 
249  // Set selection-cluster property in feature list of all selected nodes
250  // in active selection sets.
251  for (size_t i = 0; i<m_SelectionSets.size(); ++i) {
252  if (m_SelectionSets[i].GetSelected()) {
253  int cluster_id = m_SelectionSets[i].GetClusterID();
254  string cid;
255  NStr::IntToString(cid, cluster_id);
256 
257  for (size_t j = 0; j<m_SelectionSets[i].GetSelectionSet().size(); ++j) {
258  objects::CNode::TId node_id = m_SelectionSets[i].GetSelectionSet()[j];
259  CPhyloTree::TTreeIdx node_idx = tree_model->FindNodeById(node_id);
260 
261  // Node could be NULL if user edited (removed nodes from) tree or if
262  // this is a subtree which would have copied only some nodes but
263  // inherits all user data - so some ids in selection-sets may not exist.
264  // (cleaned up if missing_ids gets set to true)
265  if (node_idx != CPhyloTree::Null()) {
266  CPhyloTreeNode& node = tree_model->GetNode(node_idx);
267  node.GetValue().GetSelClusters().push_back(cluster_id);
268  string sel_clusters = node.GetValue().GetBioTreeFeatureList().GetFeatureValue(sel_feat_id);
269  if (sel_clusters.length() > 0)
270  sel_clusters += " ";
271  sel_clusters += cid;
272  node.GetValue().GetBioTreeFeatureList().SetFeature(sel_feat_id, sel_clusters);
273  }
274  }
275  }
276  }
277 }
278 
279 size_t CPhyloSelectionSetMgr::GetSelectionSet(const string& set_name)
280 {
281  for (size_t i = 0; i < m_SelectionSets.size(); ++i) {
282  if (m_SelectionSets[i].GetName() == set_name) {
283  return i;
284  }
285  }
286 
287  return size_t(-1);
288 }
289 
291 {
292  size_t set_idx = GetSelectionSet(set_name);
293 
294  if (set_idx != size_t(-1))
295  return m_SelectionSets[set_idx];
296 
297  vector<CRgbaColor> current_colors;
298  int max_cluster_id = 500;
299  for (size_t i = 0; i < m_SelectionSets.size(); ++i) {
300  current_colors.push_back(m_SelectionSets[i].GetColor());
301  max_cluster_id = std::max(m_SelectionSets[i].GetClusterID(), max_cluster_id);
302  }
303 
304  CRgbaColor c = PickBestColor(current_colors);
305 
306  vector<objects::CNode::TId> ids;
307  CPhyloSelectionSet new_set(ids, set_name);
308  new_set.SetColor(c);
309  new_set.SetClusterID(max_cluster_id);
310 
311  m_SelectionSets.push_back(new_set);
312  return m_SelectionSets[m_SelectionSets.size() - 1];
313 }
314 
316 {
317  size_t set_idx = GetSelectionSet(set_name);
318 
319  if (set_idx != size_t(-1))
320  return m_SelectionSets[set_idx];
321 
322  vector<objects::CNode::TId> ids;
323  CPhyloSelectionSet new_set(ids, set_name);
324  new_set.SetColor(c);
325 
326  m_SelectionSets.push_back(new_set);
327  return m_SelectionSets[m_SelectionSets.size() - 1];
328 }
329 
330 CRgbaColor CPhyloSelectionSetMgr::PickBestColor(const vector<CRgbaColor>& current_colors)
331 {
332  CRgbaColor color(102, 153, 204);
333  CRgbaColor white(255, 255, 255, 255);
334 
335  float num_colors = (float)current_colors.size();
336  float color_step = num_colors ? (180.0f / num_colors) : 0;
337  float rotate_angle = color_step + 100.0f;
338 
339  float max_color_dist = 0.0f;
340  CRgbaColor best_color = color;
341 
342  // Find a color that is as far away visually as possible from the other
343  // selection set colors and colors of normal clusters in the tree.
344  for (size_t i = 0; i < 2 * current_colors.size(); ++i) {
345  // protect from bright colors (almost white) since they are harder to see
346  float c_dist = CRgbaColor::ColorDistance(white, color);
347  while (c_dist < 0.7f) {
348  color.Darken(0.1f);
349  c_dist = CRgbaColor::ColorDistance(white, color);
350  }
351 
352  float min_color_dist = 1e10f;
353 
354  // Find the closest color from among all current selection/cluster colors in use
355  for (size_t j = 0; j < current_colors.size(); ++j) {
356  float dist = CRgbaColor::ColorDistance(current_colors[j], color);
357  if (dist < min_color_dist) {
358  min_color_dist = dist;
359  }
360  }
361 
362  // If the closet color is further away than the closest color
363  // for all previous colors wever tried, save the current color.
364  if (min_color_dist > max_color_dist) {
365  max_color_dist = min_color_dist;
366  best_color = color;
367  }
368 
369  color = CRgbaColor::RotateColor(color, rotate_angle);
370  }
371 
372  return best_color;
373 }
374 
376 {
377  // Selection sets may be out of sync with actual tree if the
378  // tree was created as a subtree of another tree but inherited
379  // the other trees full selections sets, or also if user
380  // directly edits selection set.
381 
382  // First remove any ids that are not in the current tree
383  for (size_t i = 0; i<m_SelectionSets.size(); ++i) {
384 
385  vector<objects::CNode::TId> selected = m_SelectionSets[i].GetSelectionSet();
386  m_SelectionSets[i].GetSelectionSet().clear();
387 
388  for (size_t j = 0; j<selected.size(); ++j) {
389  objects::CNode::TId node_id = selected[j];
390  CPhyloTree::TTreeIdx node_idx = tree_model->FindNodeById(node_id);
391 
392  if (node_idx != CPhyloTree::Null())
393  m_SelectionSets[i].GetSelectionSet().push_back(node_id);
394  }
395  }
396 
397  // Now remove any empty selection sets
398  vector<CPhyloSelectionSet> selection_sets = m_SelectionSets;
399  m_SelectionSets.clear();
400 
401  for (size_t i = 0; i<selection_sets.size(); ++i) {
402  if (selection_sets[i].GetSelectionSet().size() > 0) {
403  m_SelectionSets.push_back(selection_sets[i]);
404  }
405  }
406 }
407 
409 {
410  // set ids, first highest then lower.
411  NON_CONST_ITERATE(vector<CPhyloSelectionSet>, sel_set, m_SelectionSets) {
412  sel_set->SetClusterID(start_id--);
413  }
414 }
415 
416 
TSelClusterIDs & GetSelClusters()
CBioTreeFeatureList & GetBioTreeFeatureList()
void InitFromUserObject(CPhyloTree *tree_model, const objects::CBioTreeContainer_Base::TUser &uo)
CRgbaColor PickBestColor(const vector< CRgbaColor > &current_colors)
void SetSelectionSetProperty(CPhyloTree *tree_model)
void RenumberClusterIDs(int start_id)
Update all cluster ids using the starting-id as the lowest value.
void SaveToUserObject(objects::CBioTreeContainer_Base::TUser &uo)
size_t GetSelectionSet(const string &set_name)
Returns size_t(-1) if the name set_name is not an existing selection set.
map< int, size_t > GetClusterToSelectionMap()
CPhyloSelectionSet & AddSet(const string &set_name)
Add the new set set_name and return it. If it already exists, just return it.
void RemoveDuplicateIDs()
Remove duplicate IDs from individual sets.
void x_SyncSelectionSets(CPhyloTree *tree_model)
bool operator==(const CPhyloSelectionSetMgr &rhs) const
vector< CPhyloSelectionSet > m_SelectionSets
void SetColor(const CRgbaColor &c)
CRef< objects::CUser_object > SaveToUserObject() const
void InitFromUserObject(CRef< objects::CUser_object > sel_set)
vector< objects::CNode::TId > m_SelectedIDs
bool operator==(const CPhyloSelectionSet &rhs) const
call RemoveDuplicateIDs first which sorts and removes dups
void RemoveDuplicateIDs()
remove duplicates form m_SelectedIDs. should be called before operator==
Tree subclass also has functions and data needed for rendering and selection.
Definition: phylo_tree.hpp:52
TTreeIdx FindNodeById(TID id) const
Return index of the node with the given id or Null().
Definition: phylo_tree.hpp:307
CBioTreeFeatureDictionary & GetFeatureDict()
Return feature dictionary.
Definition: phylo_tree.hpp:323
class CRgbaColor provides a simple abstraction for managing colors.
Definition: rgba_color.hpp:58
TData & GetValue()
Return the value object for the node.
Definition: tree_model.hpp:159
static TTreeIdx Null()
Return the index value that represents a NULL node.
Definition: tree_model.hpp:678
size_t GetSize() const
Get the number of nodes currently in the array.
Definition: tree_model.hpp:218
TNodeType & GetNode(TTreeIdx idx)
Return a reference to the node at the given index.
Definition: tree_model.hpp:207
bool HasField(const string &str, const string &delim=".", NStr::ECase use_case=NStr::eCase) const
Verify that a named field exists.
CUser_object & AddField(const string &label, const string &value, EParseField parse=eParse_String)
add a data field to the user object that holds a given value
const CUser_field & GetField(const string &str, const string &delim=".", NStr::ECase use_case=NStr::eCase) const
Access a named field in this user object.
Definition: User_object.cpp:71
Definition: map.hpp:338
CRgbaColor & GetColor(CSeqFeatData::ESubtype subtype)
#define NON_CONST_ITERATE(Type, Var, Cont)
Non constant version of ITERATE macro.
Definition: ncbimisc.hpp:822
static CRgbaColor RotateColor(const CRgbaColor &c, float degrees)
Rotate the hue of the color by degrees.
static float ColorDistance(const CRgbaColor &c1, const CRgbaColor &c2)
returns the distance in the RGB color cube between the two colors, scaled to a range [0,...
void Reset(void)
Reset reference object.
Definition: ncbiobj.hpp:773
#define END_NCBI_SCOPE
End previously defined NCBI scope.
Definition: ncbistl.hpp:103
#define BEGIN_NCBI_SCOPE
Define ncbi namespace.
Definition: ncbistl.hpp:100
static int StringToInt(const CTempString str, TStringToNumFlags flags=0, int base=10)
Convert string to int.
Definition: ncbistr.cpp:630
static string IntToString(int value, TNumToStringFlags flags=0, int base=10)
Convert int to string.
Definition: ncbistr.hpp:5083
@ fConvErr_NoThrow
Do not throw an exception on error.
Definition: ncbistr.hpp:285
unsigned int TBioTreeFeatureId
Feature Id.
Definition: bio_tree.hpp:60
void SetFeature(TBioTreeFeatureId id, const string &value)
Set feature value, feature if exists replaced, if not added.
Definition: bio_tree.cpp:56
TBioTreeFeatureId Register(const string &feature_name)
Register new feature, return its id.
Definition: bio_tree.cpp:160
const string & GetFeatureValue(TBioTreeFeatureId id) const
Get feature value by id.
Definition: bio_tree.cpp:69
const TStr & GetStr(void) const
Get the variant data.
const TData & GetData(void) const
Get the Data member data.
TBool GetBool(void) const
Get the variant data.
bool IsInt(void) const
Check if variant Int is selected.
bool IsStr(void) const
Check if variant Str is selected.
TData & SetData(void)
Assign a value to Data data member.
TInt GetInt(void) const
Get the variant data.
TStr & SetStr(void)
Select the variant.
Definition: Object_id_.hpp:304
bool IsInts(void) const
Check if variant Ints is selected.
bool IsObjects(void) const
Check if variant Objects is selected.
TReal GetReal(void) const
Get the variant data.
void SetType(TType &value)
Assign a value to Type data member.
bool IsReal(void) const
Check if variant Real is selected.
bool IsBool(void) const
Check if variant Bool is selected.
const TInts & GetInts(void) const
Get the variant data.
const TObjects & GetObjects(void) const
Get the variant data.
n background color
int i
yy_size_t n
constexpr auto sort(_Init &&init)
const struct ncbi::grid::netcache::search::fields::SIZE size
T max(T x_, T y_)
Modified on Wed Dec 06 07:15:57 2023 by modify_doxy.py rev. 669887