A toolkit for working with phylogenetic data.
v0.24.0
heat_tree.cpp
Go to the documentation of this file.
1 /*
2  Genesis - A toolkit for working with phylogenetic data.
3  Copyright (C) 2014-2020 Lucas Czech and HITS gGmbH
4 
5  This program is free software: you can redistribute it and/or modify
6  it under the terms of the GNU General Public License as published by
7  the Free Software Foundation, either version 3 of the License, or
8  (at your option) any later version.
9 
10  This program is distributed in the hope that it will be useful,
11  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13  GNU General Public License for more details.
14 
15  You should have received a copy of the GNU General Public License
16  along with this program. If not, see <http://www.gnu.org/licenses/>.
17 
18  Contact:
19  Lucas Czech <lucas.czech@h-its.org>
20  Exelixis Lab, Heidelberg Institute for Theoretical Studies
21  Schloss-Wolfsbrunnenweg 35, D-69118 Heidelberg, Germany
22 */
23 
32 
38 
42 
43 #include <stdexcept>
44 
45 namespace genesis {
46 namespace tree {
47 
48 // =================================================================================================
49 // Helper Functions
50 // =================================================================================================
51 
52 static std::vector<size_t> heat_tree_row_order_(
53  Tree const& tree,
54  LayoutSpreading spreading
55 ) {
56  // Error check. For now, we only support heattrees with inner node rows for bifurcating trees.
57  if( spreading != LayoutSpreading::kLeafNodesOnly && ! is_bifurcating( tree )) {
58  throw std::invalid_argument(
59  "Tree is not bifurcating. Cannot draw heat tree with inner node rows."
60  );
61  }
62 
63  std::vector<size_t> tmp;
64  auto visits = std::vector<size_t>( tree.node_count(), 0 );
65  for( auto it : eulertour( tree )) {
66  auto const node_index = it.node().index();
67 
68  // Count the how many-th visit this is.
69  // As we have a bifurcating tree, it can never surpass 3 visits.
70  ++visits[ node_index ];
71  assert(( spreading == LayoutSpreading::kLeafNodesOnly ) || ( visits[ node_index ] <= 3 ));
72 
73  switch( spreading ) {
75  if( is_leaf( it.node() )) {
76  tmp.push_back( node_index );
77  }
78  break;
79  }
82  if( spreading == LayoutSpreading::kAllNodesButRoot && is_root( it.node() )) {
83  continue;
84  } else if( is_leaf( it.node() ) || visits[ node_index ] == 2 ) {
85  tmp.push_back( node_index );
86  }
87  break;
88  }
89  default: {
90  assert( false );
91  }
92  }
93  }
94 
95  auto const sorting = utils::sort_indices( tmp.begin(), tmp.end() );
96  auto result = std::vector<size_t>( sorting.size() );
97  for( size_t i = 0; i < sorting.size(); ++i ) {
98  assert( sorting[i] < result.size() );
99  result[ sorting[i] ] = i;
100  }
101 
102  // For each spreading, assert the correct result size.
103  assert(
104  ( spreading != LayoutSpreading::kLeafNodesOnly ) ||
105  ( result.size() == leaf_node_count( tree ))
106  );
107  assert(
108  ( spreading != LayoutSpreading::kAllNodesButRoot ) ||
109  ( result.size() == tree.node_count() - 1 )
110  );
111  assert(
112  ( spreading != LayoutSpreading::kAllNodes ) ||
113  ( result.size() == tree.node_count() )
114  );
115 
116  return result;
117 }
118 
119 template<class T>
121  utils::Matrix<T> const& mat,
122  std::vector<size_t> const& order
123 ) {
124  if( order.size() != mat.rows() ) {
125  throw std::invalid_argument( "Wrong order size for reordering matrix rows." );
126  }
127 
128  auto result = utils::Matrix<T>( mat.rows(), mat.cols() );
129  for( size_t r = 0; r < order.size(); ++r ) {
130  assert( order[r] < mat.rows() );
131  result.row(r) = mat.row( order[r] ).to_vector();
132  }
133  return result;
134 }
135 
136 // =================================================================================================
137 // SVG Functions
138 // =================================================================================================
139 
141  HeatTreeParameters const& params
142 ) {
143  // We use a dummy linear norm here, to satisfy the compiler (because the standard norm has
144  // purely virtual functiosn and can thus not be instanciated). As the color map however is
145  // empty, the called function will not use the norm.
146  return heat_tree(
148  );
149 }
150 
152  HeatTreeParameters const& params,
153  utils::ColorMap const& matrix_color_map,
154  utils::ColorNormalization const& matrix_color_norm
155 ) {
156  // We use a dummy linear norm here, to satisfy the compiler (because the standard norm has
157  // purely virtual functiosn and can thus not be instanciated). As the color map however is
158  // empty, the called function will not use the norm.
159  return heat_tree(
160  params, matrix_color_map, matrix_color_norm, {}, utils::ColorNormalizationLinear()
161  );
162 }
163 
165  HeatTreeParameters const& params,
166  utils::ColorMap const& matrix_color_map,
167  utils::ColorNormalization const& matrix_color_norm,
168  utils::ColorMap const& tree_color_map,
169  utils::ColorNormalization const& tree_color_norm
170 ) {
171  using namespace genesis::utils;
172 
173  // Prepare layout for the tree.
174  auto layout = RectangularLayout( params.tree, params.type, params.ladderize );
175  layout.height( std::max( 100.0, 6.0 * static_cast<double>( params.tree.node_count() )));
176  layout.width( layout.height() / 2.0 );
177  layout.align_labels( true );
178  layout.text_template().anchor = SvgText::Anchor::kEnd;
179 
180  // Use matrix rows to determine which inner node spreading we need.
181  if( params.matrix.rows() == params.tree.node_count() ) {
182  layout.inner_node_spreading( LayoutSpreading::kAllNodes );
183  } else if( params.matrix.rows() == params.tree.node_count() -1 ) {
184  layout.inner_node_spreading( LayoutSpreading::kAllNodesButRoot );
185  } else if( params.matrix.rows() == leaf_node_count( params.tree )) {
186  layout.inner_node_spreading( LayoutSpreading::kLeafNodesOnly );
187  } else {
188  throw std::runtime_error(
189  "Matrix has wrong size for making a heat tree. Has to be either tree.node_count(), "
190  "tree.node_count() - 1, or leaf_edge_count( tree )."
191  );
192  }
193 
194  // Set a nice spacer stroke for the needed nodes.
195  auto spacer_stroke = SvgStroke( Color( 0.8, 0.8, 0.8 ));
196  spacer_stroke.dash_array = std::vector<double>({ 2.0, 0.5 });
197  spacer_stroke.dash_offset = 2.0;
198  layout.set_label_spacer_strokes( spacer_stroke, layout.inner_node_spreading() );
199 
200  // Set edge colors.
201  if( ! params.color_per_branch.empty() ) {
202  std::vector<SvgStroke> strokes;
203  for( auto const& color : params.color_per_branch ) {
204  auto stroke = params.stroke;
205  stroke.color = color;
206  stroke.line_cap = SvgStroke::LineCap::kRound;
207  strokes.push_back( std::move( stroke ));
208  }
209  layout.set_edge_strokes( strokes );
210  }
211 
212  // Prepare svg doc.
213  auto svg_doc = layout.to_svg_document();
214  svg_doc.margin.left = svg_doc.margin.top = svg_doc.margin.bottom = svg_doc.margin.right = 200;
215 
216  // Add color scale for the tree.
217  double const svg_pal_height = svg_doc.bounding_box().width() / 10.0;
218  double const svg_pal_top = 1.2 * svg_doc.bounding_box().height();
219  if( ! tree_color_map.empty() ) {
220  // Make the scale.
221  auto svg_pal_settings = SvgColorBarSettings();
222  svg_pal_settings.direction = SvgColorBarSettings::Direction::kLeftToRight;
223  svg_pal_settings.width = layout.width(); // svg_doc.bounding_box().width();
224  svg_pal_settings.height = svg_pal_height;
225  // svg_pal_settings.text_size = svg_pal_height / 3.0;
226  auto svg_scale = make_svg_color_bar( svg_pal_settings, tree_color_map, tree_color_norm );
227 
228  // Move it to below the tree.
229  svg_scale.second.transform.append( SvgTransform::Translate(
230  0.0, svg_pal_top
231  ));
232  svg_doc.margin.bottom = 0.2 * svg_doc.bounding_box().height() + 2 * svg_pal_settings.height + 200;
233 
234  // Add it to the doc.
235  if( ! svg_scale.first.empty() ) {
236  svg_doc.defs.push_back( svg_scale.first );
237  }
238  svg_doc << std::move( svg_scale.second );
239  }
240 
241  // Sort matrix rows to fit with tree node order.
242  auto const row_order = heat_tree_row_order_( params.tree, layout.inner_node_spreading() );
243  auto const matrix = heat_tree_reorder_rows_( params.matrix, row_order );
244 
245  // Make the heat matrix, with row heights so that it fits the total tree height.
246  SvgMatrixSettings svg_mat_set;
247  svg_mat_set.pixel_height = layout.height() / static_cast<double>( matrix.rows() - 1 );
248  svg_mat_set.pixel_width = svg_mat_set.pixel_height;
249  auto svg_matrix = make_svg_matrix(
250  matrix, svg_mat_set, std::vector<std::string>{}, params.column_labels
251  );
253  svg_doc.bounding_box().width() + 20.0, - svg_mat_set.pixel_width / 2.0
254  ));
255  auto const svg_matrix_left = svg_doc.bounding_box().width() + 20.0;
256  auto const svg_matrix_width = svg_matrix.bounding_box().width();
257  svg_doc << std::move( svg_matrix );
258  svg_doc.margin.right += svg_matrix_width + 200;
259 
260  // Add color scale for the matrix.
261  if( ! matrix_color_map.empty() ) {
262  // Make the scale.
263  auto svg_pal_settings = SvgColorBarSettings();
264  svg_pal_settings.direction = SvgColorBarSettings::Direction::kLeftToRight;
265  svg_pal_settings.width = svg_matrix_width;
266  svg_pal_settings.height = svg_pal_height;
267  // svg_pal_settings.text_size = svg_pal_height / 3.0;
268  auto svg_scale = make_svg_color_bar( svg_pal_settings, matrix_color_map, matrix_color_norm );
269 
270  // Move it to below the tree.
271  svg_scale.second.transform.append( SvgTransform::Translate(
272  svg_matrix_left, svg_pal_top
273  ));
274  svg_doc.margin.bottom = 0.2 * svg_doc.bounding_box().height() + 2 * svg_pal_settings.height + 200;
275 
276  // Add it to the doc.
277  if( ! svg_scale.first.empty() ) {
278  svg_doc.defs.push_back( svg_scale.first );
279  }
280  svg_doc << std::move( svg_scale.second );
281  }
282 
283  return svg_doc;
284 }
285 
286 } // namespace tree
287 } // namespace genesis
Provides some valuable algorithms that are not part of the C++ 11 STL.
Default Color normalization, using a sequential linear scaling in the range [ min, max ].
Definition: norm_linear.hpp:59
void append(Transformation &&t)
Definition: attributes.cpp:294
SvgGroup make_svg_matrix(Matrix< Color > const &mat, SvgMatrixSettings settings, std::vector< std::string > const &row_labels, std::vector< std::string > const &col_labels)
bool is_root(TreeLink const &link)
Return whether the link belongs to the root node of its Tree.
utils::SvgDocument heat_tree(HeatTreeParameters const &params)
Definition: heat_tree.cpp:140
Store a list of colors and offer them as a map for values in range [ 0.0, 1.0 ].
Definition: map.hpp:61
bool is_bifurcating(Tree const &tree, bool loose)
Return whether the Tree is bifurcating.
Container namespace for all symbols of genesis in order to keep them separate when used as a library...
utils::Range< IteratorEulertour< true > > eulertour(ElementType const &element)
Definition: eulertour.hpp:206
std::vector< size_t > sort_indices(RandomAccessIterator first, RandomAccessIterator last, Comparator comparator)
Get the indices to the sorted order of the given range.
Definition: algorithm.hpp:182
static std::vector< size_t > heat_tree_row_order_(Tree const &tree, LayoutSpreading spreading)
Definition: heat_tree.cpp:52
std::vector< std::string > column_labels
Definition: heat_tree.hpp:61
size_t leaf_node_count(Tree const &tree)
Count the number of leaf Nodes of a Tree.
std::vector< utils::Color > color_per_branch
Definition: heat_tree.hpp:57
Class for representing phylogenetic trees.
Definition: tree/tree.hpp:97
utils::Matrix< T > heat_tree_reorder_rows_(utils::Matrix< T > const &mat, std::vector< size_t > const &order)
Definition: heat_tree.cpp:120
MatrixRow< self_type, value_type > row(size_t row)
utils::Matrix< utils::Color > matrix
Definition: heat_tree.hpp:60
Base class for color normalization.
LayoutSpreading
Spreading of the nodes of a tree for drawing.
Definition: layout_base.hpp:80
size_t node_count() const
Return the number of TreeNodes of the Tree.
Definition: tree/tree.hpp:264
SvgTransform transform
Definition: group.hpp:120
std::pair< SvgGradientLinear, SvgGroup > make_svg_color_bar(SvgColorBarSettings const &settings, ColorMap const &map, ColorNormalization const &norm, std::string const &id)
Definition: color_bar.cpp:324
bool empty() const
Return whether the Palette is empty, that is, no colors were set.
Definition: map.hpp:240
bool is_leaf(TreeLink const &link)
Return true iff the node of the given link is a leaf node.