A library for working with phylogenetic and population genetic data.
v0.27.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-2021 Lucas Czech
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 <lczech@carnegiescience.edu>
20  Department of Plant Biology, Carnegie Institution For Science
21  260 Panama Street, Stanford, CA 94305, USA
22 */
23 
32 
38 
43 
44 #include <stdexcept>
45 
46 namespace genesis {
47 namespace tree {
48 
49 // =================================================================================================
50 // Row Order Helpers
51 // =================================================================================================
52 
53 static std::vector<size_t> heat_tree_row_order_(
54  Tree const& tree,
55  LayoutSpreading spreading
56 ) {
57  // Error check. For now, we only support heattrees with inner node rows for bifurcating trees.
58  if( spreading != LayoutSpreading::kLeafNodesOnly && ! is_bifurcating( tree )) {
59  throw std::invalid_argument(
60  "Tree is not bifurcating. Cannot draw heat tree with inner node rows."
61  );
62  }
63 
64  std::vector<size_t> tmp;
65  auto visits = std::vector<size_t>( tree.node_count(), 0 );
66  for( auto it : eulertour( tree )) {
67  auto const node_index = it.node().index();
68 
69  // Count the how many-th visit this is.
70  // As we have a bifurcating tree, it can never surpass 3 visits.
71  ++visits[ node_index ];
72  assert(( spreading == LayoutSpreading::kLeafNodesOnly ) || ( visits[ node_index ] <= 3 ));
73 
74  switch( spreading ) {
76  if( is_leaf( it.node() )) {
77  tmp.push_back( node_index );
78  }
79  break;
80  }
83  if( spreading == LayoutSpreading::kAllNodesButRoot && is_root( it.node() )) {
84  continue;
85  } else if( is_leaf( it.node() ) || visits[ node_index ] == 2 ) {
86  tmp.push_back( node_index );
87  }
88  break;
89  }
90  default: {
91  assert( false );
92  }
93  }
94  }
95 
96  auto const sorting = utils::sort_indices( tmp.begin(), tmp.end() );
97  auto result = std::vector<size_t>( sorting.size() );
98  for( size_t i = 0; i < sorting.size(); ++i ) {
99  assert( sorting[i] < result.size() );
100  result[ sorting[i] ] = i;
101  }
102 
103  // For each spreading, assert the correct result size.
104  assert(
105  ( spreading != LayoutSpreading::kLeafNodesOnly ) ||
106  ( result.size() == leaf_node_count( tree ))
107  );
108  assert(
109  ( spreading != LayoutSpreading::kAllNodesButRoot ) ||
110  ( result.size() == tree.node_count() - 1 )
111  );
112  assert(
113  ( spreading != LayoutSpreading::kAllNodes ) ||
114  ( result.size() == tree.node_count() )
115  );
116 
117  return result;
118 }
119 
120 template<class T>
122  utils::Matrix<T> const& mat,
123  std::vector<size_t> const& order
124 ) {
125  if( order.size() != mat.rows() ) {
126  throw std::invalid_argument( "Wrong order size for reordering matrix rows." );
127  }
128 
129  auto result = utils::Matrix<T>( mat.rows(), mat.cols() );
130  for( size_t r = 0; r < order.size(); ++r ) {
131  assert( order[r] < mat.rows() );
132  result.row(r) = mat.row( order[r] ).to_vector();
133  }
134  return result;
135 }
136 
137 // =================================================================================================
138 // Internal Functions
139 // =================================================================================================
140 
144 struct HeatTreeGrid
145 {
149  double pal_top;
150 
154  double pal_height;
155 
159  double matrix_left;
160 
164  double matrix_width;
165 };
166 
168  HeatTreeParameters const& params
169 ) {
170  using namespace genesis::utils;
171 
172  // Prepare layout for the tree.
173  auto layout = RectangularLayout( params.tree, params.type, params.ladderize );
174  layout.height( std::max( 100.0, 6.0 * static_cast<double>( params.tree.node_count() )));
175  layout.width( layout.height() / 2.0 );
176  layout.align_labels( true );
177  layout.text_template().anchor = SvgText::Anchor::kEnd;
178 
179  // Use matrix rows to determine which inner node spreading we need.
180  if( params.matrix.rows() == params.tree.node_count() ) {
181  layout.inner_node_spreading( LayoutSpreading::kAllNodes );
182  } else if( params.matrix.rows() == params.tree.node_count() -1 ) {
183  layout.inner_node_spreading( LayoutSpreading::kAllNodesButRoot );
184  } else if( params.matrix.rows() == leaf_node_count( params.tree )) {
185  layout.inner_node_spreading( LayoutSpreading::kLeafNodesOnly );
186  } else {
187  throw std::runtime_error(
188  "Matrix has wrong size for making a heat tree. Has to be either tree.node_count(), "
189  "tree.node_count() - 1, or leaf_edge_count( tree )."
190  );
191  }
192 
193  // Set a nice spacer stroke for the needed nodes.
194  auto spacer_stroke = SvgStroke( Color( 0.8, 0.8, 0.8 ));
195  spacer_stroke.dash_array = std::vector<double>({ 2.0, 0.5 });
196  spacer_stroke.dash_offset = 2.0;
197  layout.set_label_spacer_strokes( spacer_stroke, layout.inner_node_spreading() );
198 
199  // Set edge colors.
200  if( ! params.color_per_branch.empty() ) {
201  std::vector<SvgStroke> strokes;
202  for( auto const& color : params.color_per_branch ) {
203  auto stroke = params.stroke;
204  stroke.color = color;
205  stroke.line_cap = SvgStroke::LineCap::kRound;
206  strokes.push_back( std::move( stroke ));
207  }
208  layout.set_edge_strokes( strokes );
209  }
210 
211  return layout;
212 }
213 
215  RectangularLayout const& layout,
216  utils::ColorMap const& tree_color_map,
217  utils::ColorNormalization const& tree_color_norm,
218  utils::SvgDocument& svg_doc,
219  HeatTreeGrid& grid
220 ) {
221  using namespace genesis::utils;
222 
223  // Add color scale for the tree.
224  double const svg_pal_top = 1.2 * svg_doc.bounding_box().height();
225  double const svg_pal_height = svg_doc.bounding_box().width() / 10.0;
226  if( ! tree_color_map.empty() ) {
227  // Make the scale.
228  auto svg_pal_settings = SvgColorBarSettings();
229  svg_pal_settings.direction = SvgColorBarSettings::Direction::kLeftToRight;
230  svg_pal_settings.width = layout.width(); // svg_doc.bounding_box().width();
231  svg_pal_settings.height = svg_pal_height;
232  // svg_pal_settings.text_size = svg_pal_height / 3.0;
233  auto svg_scale = make_svg_color_bar( svg_pal_settings, tree_color_map, tree_color_norm );
234 
235  // Move it to below the tree.
236  svg_scale.second.transform.append( SvgTransform::Translate(
237  0.0, svg_pal_top
238  ));
239  svg_doc.margin.bottom = 0.2 * svg_doc.bounding_box().height() + 2 * svg_pal_settings.height + 200;
240 
241  // Add it to the doc.
242  if( ! svg_scale.first.empty() ) {
243  svg_doc.defs.push_back( svg_scale.first );
244  }
245  svg_doc << std::move( svg_scale.second );
246  }
247 
248  // The height of the tree (plus some space) is what we use as the top coordinate for the
249  // gradients. We store this, so that the matrix gradient can use this as well.
250  grid.pal_top = svg_pal_top;
251  grid.pal_height = svg_pal_height;
252 }
253 
255  HeatTreeParameters const& params,
256  RectangularLayout const& layout,
257  utils::Matrix<utils::Color> const& matrix,
258  utils::SvgDocument& svg_doc,
259  HeatTreeGrid& grid
260 ) {
261  using namespace genesis::utils;
262 
263  // Make the heat matrix, with row heights so that it fits the total tree height.
264  SvgMatrixSettings svg_mat_set;
265  svg_mat_set.pixel_height = layout.height() / static_cast<double>( matrix.rows() - 1 );
266  svg_mat_set.pixel_width = svg_mat_set.pixel_height;
267  auto svg_matrix = make_svg_matrix(
268  matrix, svg_mat_set, std::vector<std::string>{}, params.column_labels
269  );
271  svg_doc.bounding_box().width() + 20.0, - svg_mat_set.pixel_width / 2.0
272  ));
273  auto const svg_matrix_left = svg_doc.bounding_box().width() + 20.0;
274  auto const svg_matrix_width = svg_matrix.bounding_box().width();
275  svg_doc << std::move( svg_matrix );
276  svg_doc.margin.right += svg_matrix_width + 200;
277 
278  // Lastly, set the needed grid params, so that downstream functions can use them.
279  grid.matrix_left = svg_matrix_left;
280  grid.matrix_width = svg_matrix_width;
281 }
282 
284  HeatTreeParameters const& params,
285  RectangularLayout const& layout,
286  utils::Matrix<utils::Color> const& matrix,
287  utils::SvgDocument& svg_doc,
288  HeatTreeGrid& grid
289 ) {
290  using namespace genesis::utils;
291 
292  // TODO
293  (void) params.column_labels;
294 
295  // Make a bmp version of the matrix.
296  std::string matrix_bmp;
297  BmpWriter().write( matrix, to_string( matrix_bmp ));
298 
299  // Get position and scale for the matrix.
300  auto const pixel_height = layout.height() / static_cast<double>( matrix.rows() - 1 );
301  auto const pixel_width = pixel_height;
302  auto const offset_x = svg_doc.bounding_box().width() + 20.0;
303  auto const offset_y = - pixel_width / 2.0;
304 
305  // Add to svg.
306  auto img = SvgImage(
307  svg_data_uri( "image/bmp", matrix_bmp, true ),
308  SvgPoint{ offset_x, offset_y },
309  SvgSize{
310  static_cast<double>( matrix.cols() ) * pixel_width,
311  static_cast<double>( matrix.rows() ) * pixel_height
312  }
313  );
314  img.rendering = SvgImage::ImageRendering::kPixelated;
315  svg_doc << std::move( img );
316  svg_doc.margin.right += static_cast<double>( matrix.cols() ) * pixel_width + 200;
317 
318  // Lastly, set the needed grid params, so that downstream functions can use them.
319  grid.matrix_left = offset_x;
320  grid.matrix_width = static_cast<double>( matrix.cols() ) * pixel_width;
321 }
322 
324  utils::ColorMap const& matrix_color_map,
325  utils::ColorNormalization const& matrix_color_norm,
326  utils::SvgDocument& svg_doc,
327  HeatTreeGrid& grid
328 ) {
329  using namespace genesis::utils;
330 
331  // Add color scale for the matrix.
332  if( ! matrix_color_map.empty() ) {
333  // Make the scale.
334  auto svg_pal_settings = SvgColorBarSettings();
335  svg_pal_settings.direction = SvgColorBarSettings::Direction::kLeftToRight;
336  svg_pal_settings.width = grid.matrix_width;
337  svg_pal_settings.height = grid.pal_height;
338  // svg_pal_settings.text_size = svg_pal_height / 3.0;
339  auto svg_scale = make_svg_color_bar( svg_pal_settings, matrix_color_map, matrix_color_norm );
340 
341  // Move it to below the tree.
342  svg_scale.second.transform.append( SvgTransform::Translate(
343  grid.matrix_left, grid.pal_top
344  ));
345  svg_doc.margin.bottom = 0.2 * svg_doc.bounding_box().height() + 2 * svg_pal_settings.height + 200;
346 
347  // Add it to the doc.
348  if( ! svg_scale.first.empty() ) {
349  svg_doc.defs.push_back( svg_scale.first );
350  }
351  svg_doc << std::move( svg_scale.second );
352  }
353 }
354 
355 // =================================================================================================
356 // SVG Functions
357 // =================================================================================================
358 
360  HeatTreeParameters const& params
361 ) {
362  // We use a dummy linear norm here, to satisfy the compiler (because the standard norm has
363  // purely virtual functiosn and can thus not be instanciated). As the color map however is
364  // empty, the called function will not use the norm.
365  return heat_tree(
367  );
368 }
369 
371  HeatTreeParameters const& params,
372  utils::ColorMap const& matrix_color_map,
373  utils::ColorNormalization const& matrix_color_norm
374 ) {
375  // We use a dummy linear norm here, to satisfy the compiler (because the standard norm has
376  // purely virtual functiosn and can thus not be instanciated). As the color map however is
377  // empty, the called function will not use the norm.
378  return heat_tree(
379  params, matrix_color_map, matrix_color_norm, {}, utils::ColorNormalizationLinear()
380  );
381 }
382 
384  HeatTreeParameters const& params,
385  utils::ColorMap const& matrix_color_map,
386  utils::ColorNormalization const& matrix_color_norm,
387  utils::ColorMap const& tree_color_map,
388  utils::ColorNormalization const& tree_color_norm
389 ) {
390  using namespace genesis::utils;
391  HeatTreeGrid grid;
392 
393  // Get the tree layout, including colored branches.
394  auto layout = heat_tree_tree_layout_( params );
395 
396  // Prepare svg doc where all elements are added to.
397  // This already includes the tree drawing.
398  auto svg_doc = layout.to_svg_document();
399  svg_doc.margin.left = svg_doc.margin.top = svg_doc.margin.bottom = svg_doc.margin.right = 200;
400 
401  // Add color scale for the tree.
403  layout, tree_color_map, tree_color_norm, svg_doc, grid
404  );
405 
406  // Sort matrix rows to fit with tree node order.
407  auto const row_order = heat_tree_row_order_( params.tree, layout.inner_node_spreading() );
408  auto const matrix = heat_tree_reorder_rows_( params.matrix, row_order );
409 
410  // Make the heat matrix, with row heights so that it fits the total tree height.
411  if( params.matrix_as_bmp ) {
412  heat_tree_add_heat_matrix_bmp_( params, layout, matrix, svg_doc, grid );
413  } else {
414  heat_tree_add_heat_matrix_svg_( params, layout, matrix, svg_doc, grid );
415  }
416 
417  // Add color scale for the matrix.
419  matrix_color_map, matrix_color_norm, svg_doc, grid
420  );
421 
422  return svg_doc;
423 }
424 
425 } // namespace tree
426 } // namespace genesis
genesis::utils::SvgMargin::bottom
double bottom
Definition: utils/formats/svg/helper.hpp:108
genesis::utils::ColorMap
Store a list of colors and offer them as a map for values in range [ 0.0, 1.0 ].
Definition: map.hpp:61
genesis::utils::svg_data_uri
std::string svg_data_uri(std::string const &media_type, std::string const &content, bool encode_base64=false)
Definition: utils/formats/svg/helper.hpp:262
algorithm.hpp
Provides some valuable algorithms that are not part of the C++ 11 STL.
genesis::utils::Color
Definition: color.hpp:47
genesis::utils::SvgImage
Definition: image.hpp:47
genesis::tree::leaf_node_count
size_t leaf_node_count(Tree const &tree)
Count the number of leaf Nodes of a Tree.
Definition: tree/function/functions.cpp:168
genesis::tree::RectangularLayout::height
RectangularLayout & height(double const value)
Definition: rectangular_layout.cpp:58
genesis::tree::Tree::node_count
size_t node_count() const
Return the number of TreeNodes of the Tree.
Definition: tree/tree.hpp:264
genesis::utils::SvgTransform::append
void append(Transformation &&t)
Definition: attributes.cpp:294
genesis::utils::Matrix::cols
size_t cols() const
Definition: containers/matrix.hpp:167
genesis::utils::make_svg_color_bar
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
genesis::tree::HeatTreeParameters::stroke
utils::SvgStroke stroke
Definition: heat_tree.hpp:59
genesis::tree::LayoutSpreading::kLeafNodesOnly
@ kLeafNodesOnly
genesis::tree::HeatTreeParameters
Definition: heat_tree.hpp:52
genesis::utils::SvgDocument::margin
SvgMargin margin
Definition: svg/document.hpp:120
genesis::utils::ColorNormalization
Base class for color normalization.
Definition: normalization.hpp:52
genesis::tree::RectangularLayout
Definition: rectangular_layout.hpp:47
eulertour.hpp
genesis::utils::make_svg_matrix
SvgGroup make_svg_matrix(Matrix< Color > const &mat, SvgMatrixSettings settings, std::vector< std::string > const &row_labels, std::vector< std::string > const &col_labels)
Definition: formats/svg/matrix.cpp:50
genesis::tree::HeatTreeParameters::matrix_as_bmp
bool matrix_as_bmp
Definition: heat_tree.hpp:64
functions.hpp
genesis::utils::sort_indices
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
genesis::tree::HeatTreeParameters::ladderize
bool ladderize
Definition: heat_tree.hpp:57
genesis::tree::heat_tree_add_matrix_color_scale_
void heat_tree_add_matrix_color_scale_(utils::ColorMap const &matrix_color_map, utils::ColorNormalization const &matrix_color_norm, utils::SvgDocument &svg_doc, HeatTreeGrid &grid)
Definition: heat_tree.cpp:323
layout_base.hpp
genesis::tree::HeatTreeParameters::column_labels
std::vector< std::string > column_labels
Definition: heat_tree.hpp:63
genesis::utils
Definition: placement/formats/edge_color.hpp:42
genesis::utils::SvgBox::width
double width() const
Definition: utils/formats/svg/helper.hpp:146
genesis::tree::HeatTreeParameters::color_per_branch
std::vector< utils::Color > color_per_branch
Definition: heat_tree.hpp:58
genesis::utils::Matrix
Definition: placement/function/emd.hpp:53
genesis::population::to_string
std::string to_string(GenomeLocus const &locus)
Definition: functions/genome_locus.hpp:48
genesis::tree::heat_tree_add_heat_matrix_svg_
void heat_tree_add_heat_matrix_svg_(HeatTreeParameters const &params, RectangularLayout const &layout, utils::Matrix< utils::Color > const &matrix, utils::SvgDocument &svg_doc, HeatTreeGrid &grid)
Definition: heat_tree.cpp:254
genesis::utils::SvgPoint
Definition: utils/formats/svg/helper.hpp:51
genesis::utils::SvgStroke
Definition: attributes.hpp:49
genesis::tree::heat_tree_add_heat_matrix_bmp_
void heat_tree_add_heat_matrix_bmp_(HeatTreeParameters const &params, RectangularLayout const &layout, utils::Matrix< utils::Color > const &matrix, utils::SvgDocument &svg_doc, HeatTreeGrid &grid)
Definition: heat_tree.cpp:283
genesis::utils::SvgMatrixSettings::pixel_height
double pixel_height
Definition: formats/svg/matrix.hpp:65
genesis::tree::Tree
Class for representing phylogenetic trees.
Definition: tree/tree.hpp:97
genesis::tree::LayoutSpreading::kAllNodes
@ kAllNodes
genesis::utils::SvgTransform::Translate
Definition: attributes.hpp:252
genesis::utils::SvgMatrixSettings::pixel_width
double pixel_width
Definition: formats/svg/matrix.hpp:64
genesis::tree::HeatTreeParameters::type
LayoutType type
Definition: heat_tree.hpp:56
genesis::utils::ColorNormalizationLinear
Default Color normalization, using a sequential linear scaling in the range [ min,...
Definition: norm_linear.hpp:59
genesis::utils::BmpWriter::write
void write(Matrix< Color > const &image, std::shared_ptr< utils::BaseOutputTarget > target) const
Write a full 24bit RGB Color image to an output target.
Definition: utils/formats/bmp/writer.cpp:53
genesis::tree::is_root
bool is_root(TreeLink const &link)
Return whether the link belongs to the root node of its Tree.
Definition: tree/function/functions.cpp:89
rectangular_layout.hpp
genesis::tree::heat_tree_tree_layout_
RectangularLayout heat_tree_tree_layout_(HeatTreeParameters const &params)
Definition: heat_tree.cpp:167
genesis::utils::SvgMatrixSettings
Definition: formats/svg/matrix.hpp:56
genesis
Container namespace for all symbols of genesis in order to keep them separate when used as a library.
Definition: placement/formats/edge_color.cpp:42
genesis::tree::heat_tree_add_tree_color_scale_
void heat_tree_add_tree_color_scale_(RectangularLayout const &layout, utils::ColorMap const &tree_color_map, utils::ColorNormalization const &tree_color_norm, utils::SvgDocument &svg_doc, HeatTreeGrid &grid)
Definition: heat_tree.cpp:214
genesis::utils::SvgImage::rendering
ImageRendering rendering
Definition: image.hpp:134
genesis::utils::SvgBox::height
double height() const
Definition: utils/formats/svg/helper.hpp:151
genesis::utils::SvgMargin::right
double right
Definition: utils/formats/svg/helper.hpp:107
genesis::utils::SvgColorBarSettings
Definition: color_bar.hpp:59
genesis::utils::SvgSize
Definition: utils/formats/svg/helper.hpp:66
genesis::utils::BmpWriter
Write Bitmap image files.
Definition: utils/formats/bmp/writer.hpp:60
genesis::tree::heat_tree_reorder_rows_
utils::Matrix< T > heat_tree_reorder_rows_(utils::Matrix< T > const &mat, std::vector< size_t > const &order)
Definition: heat_tree.cpp:121
genesis::tree::HeatTreeParameters::tree
CommonTree tree
Definition: heat_tree.hpp:55
norm_linear.hpp
genesis::utils::SvgDocument
Definition: svg/document.hpp:49
genesis::utils::SvgDocument::defs
std::vector< SvgDefinitions > defs
Definition: svg/document.hpp:123
genesis::tree::is_bifurcating
bool is_bifurcating(Tree const &tree, bool loose)
Return whether the Tree is bifurcating.
Definition: tree/function/functions.cpp:134
genesis::utils::ColorMap::empty
bool empty() const
Return whether the Palette is empty, that is, no colors were set.
Definition: map.hpp:240
genesis::tree::HeatTreeParameters::matrix
utils::Matrix< utils::Color > matrix
Definition: heat_tree.hpp:62
genesis::utils::SvgDocument::bounding_box
SvgBox bounding_box() const
Definition: svg/document.cpp:57
writer.hpp
genesis::utils::SvgGroup::transform
SvgTransform transform
Definition: group.hpp:154
heat_tree.hpp
matrix.hpp
genesis::tree::heat_tree
utils::SvgDocument heat_tree(HeatTreeParameters const &params)
Definition: heat_tree.cpp:359
genesis::tree::eulertour
utils::Range< IteratorEulertour< true > > eulertour(ElementType const &element)
Definition: eulertour.hpp:206
genesis::utils::Matrix::row
MatrixRow< self_type, value_type > row(size_t row)
Definition: containers/matrix.hpp:221
genesis::tree::RectangularLayout::width
RectangularLayout & width(double const value)
Definition: rectangular_layout.cpp:47
layout_tree.hpp
genesis::tree::is_leaf
bool is_leaf(TreeLink const &link)
Return true iff the node of the given link is a leaf node.
Definition: tree/function/functions.cpp:59
genesis::utils::Matrix::rows
size_t rows() const
Definition: containers/matrix.hpp:162
genesis::tree::LayoutSpreading::kAllNodesButRoot
@ kAllNodesButRoot
genesis::tree::heat_tree_row_order_
static std::vector< size_t > heat_tree_row_order_(Tree const &tree, LayoutSpreading spreading)
Definition: heat_tree.cpp:53
genesis::tree::LayoutSpreading
LayoutSpreading
Spreading of the nodes of a tree for drawing.
Definition: layout_base.hpp:80
genesis::utils::SvgStroke::color
Color color
Definition: attributes.hpp:113