Skip to content

Commit

Permalink
Merge pull request #557 from pangenome/odgi_draw_svg_labels
Browse files Browse the repository at this point in the history
`odgi draw`: add labels in the SVG output
  • Loading branch information
AndreaGuarracino authored Feb 10, 2024
2 parents 519cc18 + 8418ea3 commit 7bbd41d
Show file tree
Hide file tree
Showing 3 changed files with 58 additions and 9 deletions.
44 changes: 42 additions & 2 deletions src/algorithms/draw.cpp
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#include "draw.hpp"
#include "split.hpp"

namespace odgi {

Expand Down Expand Up @@ -98,15 +99,29 @@ void get_layout(const std::vector<double> &X,

}


struct label_info_t {
double x, y;
std::string content;
// Simple constructor for convenience
label_info_t(double x, double y, std::string content) : x(x), y(y), content(std::move(content)) {}
};
bool is_too_close(double x, double y, const std::string& content, double threshold, std::vector<label_info_t>& placed_labels) {
for (const auto& label : placed_labels) {
if (label.content == content && std::abs(label.x - x) < threshold && std::abs(label.y - y) < threshold) {
return true; // Found a label too close with the same content
}
}
return false;
}
void draw_svg(std::ostream &out,
const std::vector<double> &X,
const std::vector<double> &Y,
const PathHandleGraph &graph,
const double& scale,
const double& border,
const double& line_width,
std::vector<algorithms::color_t>& node_id_to_color) {
std::vector<algorithms::color_t>& node_id_to_color,
ska::flat_hash_map<handlegraph::nid_t, std::set<std::string>>& node_id_to_label_map) {

std::vector<std::vector<handle_t>> weak_components;
coord_range_2d_t rendered_range;
Expand All @@ -121,6 +136,8 @@ void draw_svg(std::ostream &out,
double width = rendered_range.width();
double height = rendered_range.height();

std::vector<label_info_t> placed_labels;

out << std::setprecision(std::numeric_limits<double>::digits10 + 1);
out << "<svg width=\"" << width << "\" height=\"" << height << "\" "
<< "viewBox=\"" << viewbox_x1 << " " << viewbox_y1
Expand Down Expand Up @@ -160,6 +177,29 @@ void draw_svg(std::ostream &out,
} else {
highlights.push_back(handle);
}

double x = (X[a] * scale) - x_off;
double y = (Y[a] * scale) + y_off;
// Check if this is a node with a label
if (node_id_to_label_map.count(graph.get_id(handle))){
// Collect the labels that can be put without overlapping identical ones
std::vector<std::string> labels;
for (auto text : node_id_to_label_map[graph.get_id(handle)]){
if (!is_too_close(x, y, text, 30.0, placed_labels)) {
labels.push_back(text);
}
}
// Check if there is something to label
if (!labels.empty()){
out << "<text font-family=\"Arial\" font-size=\"20\" fill=\"#000000\" stroke=\"#000000\" y=\"" << y << "\">";
for (auto text : labels){
out << "<tspan x=\"" << x << "\" dy=\"1.0em\">" << text << "</tspan>";
placed_labels.emplace_back(x, y, text); // Record the label's placement
}
out << "</text>"
<< std::endl;
}
}
}

// color highlights
Expand Down
3 changes: 2 additions & 1 deletion src/algorithms/draw.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,8 @@ void draw_svg(std::ostream &out,
const double& scale,
const double& border,
const double& line_width,
std::vector<algorithms::color_t>& node_id_to_color);
std::vector<algorithms::color_t>& node_id_to_color,
ska::flat_hash_map<handlegraph::nid_t, std::set<std::string>>& node_id_to_label_map);

std::vector<uint8_t> rasterize(const std::vector<double> &X,
const std::vector<double> &Y,
Expand Down
20 changes: 14 additions & 6 deletions src/subcommand/draw_main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,8 @@ int main_draw(int argc, char **argv) {
// handle targets from BED
std::vector<odgi::path_range_t> path_ranges;
std::vector<algorithms::color_t> node_id_to_color;
ska::flat_hash_map<handlegraph::nid_t, std::set<std::string>> node_id_to_label_map; // To remember the unique node to label for each path range

if (_path_bed_file && !args::get(_path_bed_file).empty()) {
std::ifstream bed_in(args::get(_path_bed_file));
std::string line;
Expand All @@ -137,6 +139,8 @@ int main_draw(int argc, char **argv) {
if (!path_range.name.empty()) {
auto vals = split(path_range.name, '#');
if (vals.size() == 2 && vals[1].length() == 6) {
path_range.name = vals[0]; // Remove the color from the name

// Colors are given in RRGGBB in the BED file, but they are taken in BBGGRR, so we need to switch BB/RR

char temp = vals[1][0];
Expand All @@ -157,14 +161,18 @@ int main_draw(int argc, char **argv) {
}
}





bool first_handle_taken = path_range.name.empty(); // To avoid checking if there is no name to take
algorithms::for_handle_in_path_range(
graph, path_handle, path_range.begin.offset, path_range.end.offset,
[&](const handle_t& handle) {
node_id_to_color[graph.get_id(handle)] = path_color;
const auto node_id = graph.get_id(handle);
node_id_to_color[node_id] = path_color;

if (!first_handle_taken) {
first_handle_taken = true;
// The set automatically handles uniqueness of labels within the set.
node_id_to_label_map[node_id].insert(path_range.name);
}
});
}
}
Expand Down Expand Up @@ -216,7 +224,7 @@ int main_draw(int argc, char **argv) {
// todo could be done with callbacks
std::vector<double> X = layout.get_X();
std::vector<double> Y = layout.get_Y();
algorithms::draw_svg(f, X, Y, graph, svg_scale, border_bp, _png_line_width, node_id_to_color);
algorithms::draw_svg(f, X, Y, graph, svg_scale, border_bp, _png_line_width, node_id_to_color, node_id_to_label_map);
f.close();
}

Expand Down

0 comments on commit 7bbd41d

Please sign in to comment.