Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

odgi draw: add labels in the SVG output #557

Merged
merged 3 commits into from
Feb 10, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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