@@ -60,27 +60,30 @@ structure_trnas <- function(organism) {
6060# ' @param organism Character string specifying the organism name
6161# ' (e.g., `"Escherichia coli"`).
6262# ' @param modifications A tibble with columns `pos` (1-based
63- # ' position in the tRNA sequence) and `mod1` (short modification
64- # ' name, e.g., `"m1A"`). Output of [modomics_mods()] works
65- # ' directly after filtering to the tRNA of interest.
66- # ' @param outlines A tibble with columns `pos` (1-based position)
67- # ' and `group` (category name for palette lookup). Draws circle
68- # ' outlines (stroke only, no fill) around each nucleotide.
69- # ' @param linkages A tibble with columns `pos1`, `pos2`, and
70- # ' optionally `value` (e.g., log odds ratio) for coloring arcs.
71- # ' If a `log_odds_ratio` column is present and `value` is not, it
72- # ' is automatically used as `value`, so output of
73- # ' [clean_odds_ratios()] or [filter_linkages()] works directly.
63+ # ' position or Sprinzl label when `sprinzl_coords` is provided)
64+ # ' and `mod1` (short modification name, e.g., `"m1A"`). Output
65+ # ' of [modomics_mods()] works directly after filtering to the
66+ # ' tRNA of interest.
67+ # ' @param outlines A tibble with columns `pos` (1-based position
68+ # ' or Sprinzl label) and `group` (category name for palette
69+ # ' lookup). Draws circle outlines (stroke only, no fill) around
70+ # ' each nucleotide.
71+ # ' @param linkages A tibble with columns `pos1`, `pos2` (1-based
72+ # ' positions or Sprinzl labels), and optionally `value` (e.g.,
73+ # ' log odds ratio) for coloring arcs. If a `log_odds_ratio`
74+ # ' column is present and `value` is not, it is automatically
75+ # ' used as `value`, so output of [clean_odds_ratios()] or
76+ # ' [filter_linkages()] works directly.
7477# ' @param output Path for the output SVG file. If `NULL` (default),
7578# ' writes to a temporary file.
7679# ' @param mod_palette Named character vector of colors keyed by
7780# ' modification short name. If `NULL`, uses a default palette.
7881# ' @param outline_palette Named character vector of colors keyed by
7982# ' outline group name. If `NULL`, uses `"#333333"` for all.
80- # ' @param text_colors A tibble with columns `pos` (1-based position)
81- # ' and `color` (hex color string). Changes the nucleotide letter
82- # ' color at specified positions. Unspecified positions keep the
83- # ' default color.
83+ # ' @param text_colors A tibble with columns `pos` (1-based position
84+ # ' or Sprinzl label) and `color` (hex color string). Changes the
85+ # ' nucleotide letter color at specified positions. Unspecified
86+ # ' positions keep the default color.
8487# ' @param position_markers Logical; if `TRUE` (default), draw
8588# ' small grey position numbers every 10 nucleotides around the
8689# ' cloverleaf to help orient readers.
@@ -89,6 +92,16 @@ structure_trnas <- function(organism) {
8992# ' linkage values. Default `c("#0072B2", "#D55E00")` (blue for
9093# ' exclusive, vermillion for co-occurring). Stroke width encodes
9194# ' the magnitude of the value.
95+ # ' @param sprinzl_coords A tibble of Sprinzl coordinates as
96+ # ' returned by [read_sprinzl_coords()], or `NULL` (default). When
97+ # ' provided, position columns in `modifications`, `outlines`,
98+ # ' `text_colors`, and `linkages` are interpreted as Sprinzl
99+ # ' labels and converted to 1-based sequence positions
100+ # ' automatically.
101+ # ' @param trna_id Character string identifying the tRNA in
102+ # ' `sprinzl_coords` (e.g.,
103+ # ' `"nuc-tRNA-Glu-UUC-1-1"`). If `NULL` (default), the tRNA
104+ # ' name is resolved from `trna` automatically.
92105# '
93106# ' @return The path to the annotated SVG file (invisibly).
94107# '
@@ -109,10 +122,56 @@ plot_tRNA_structure <- function(
109122 outline_palette = NULL ,
110123 text_colors = NULL ,
111124 position_markers = TRUE ,
112- linkage_palette = c(" #0072B2" , " #D55E00" )
125+ linkage_palette = c(" #0072B2" , " #D55E00" ),
126+ sprinzl_coords = NULL ,
127+ trna_id = NULL
113128) {
114129 rlang :: check_installed(" jsonlite" , reason = " to read structure metadata." )
115130
131+ if (! is.null(sprinzl_coords )) {
132+ if (is.null(trna_id )) {
133+ trna_id <- find_sprinzl_id(trna , sprinzl_coords )
134+ if (is.null(trna_id )) {
135+ # Fallback: try matching without "nuc-" prefix
136+ trna_id <- find_sprinzl_id_bare(trna , sprinzl_coords )
137+ }
138+ if (is.null(trna_id )) {
139+ cli :: cli_abort(
140+ " Could not find {.val {trna}} in {.arg sprinzl_coords}."
141+ )
142+ }
143+ }
144+ trna_coords <- sprinzl_coords [sprinzl_coords $ trna_id == trna_id , ]
145+ if (! is.null(modifications )) {
146+ modifications <- convert_sprinzl_positions(
147+ modifications ,
148+ " pos" ,
149+ trna_coords
150+ )
151+ }
152+ if (! is.null(outlines )) {
153+ outlines <- convert_sprinzl_positions(
154+ outlines ,
155+ " pos" ,
156+ trna_coords
157+ )
158+ }
159+ if (! is.null(text_colors )) {
160+ text_colors <- convert_sprinzl_positions(
161+ text_colors ,
162+ " pos" ,
163+ trna_coords
164+ )
165+ }
166+ if (! is.null(linkages )) {
167+ linkages <- convert_sprinzl_positions(
168+ linkages ,
169+ c(" pos1" , " pos2" ),
170+ trna_coords
171+ )
172+ }
173+ }
174+
116175 org_dir <- structure_org_dir(organism )
117176
118177 svg_path <- file.path(org_dir , paste0(trna , " .svg" ))
@@ -296,6 +355,39 @@ structure_html <- function(svg_path) {
296355
297356# Internal helpers -------------------------------------------------------------
298357
358+ find_sprinzl_id_bare <- function (trna , sprinzl_coords ) {
359+ parts <- strsplit(trna , " -" )[[1 ]]
360+ if (length(parts ) > = 3 ) {
361+ parts [3 ] <- gsub(" T" , " U" , parts [3 ])
362+ }
363+ rna_name <- paste(parts , collapse = " -" )
364+ pattern <- paste0(" ^" , rna_name , " -" )
365+
366+ ids <- unique(sprinzl_coords $ trna_id )
367+ matches <- grep(pattern , ids , value = TRUE )
368+ if (length(matches ) == 0 ) {
369+ return (NULL )
370+ }
371+ sort(matches )[1 ]
372+ }
373+
374+ convert_sprinzl_positions <- function (df , pos_cols , trna_coords ) {
375+ lookup <- trna_coords [, c(" sprinzl_label" , " pos" )]
376+ for (col in pos_cols ) {
377+ original <- as.character(df [[col ]])
378+ matched <- lookup $ pos [match(original , lookup $ sprinzl_label )]
379+ unmatched <- original [is.na(matched ) & ! is.na(original )]
380+ if (length(unmatched ) > 0 ) {
381+ n <- length(unmatched )
382+ cli :: cli_warn(
383+ " Sprinzl position{cli::qty(length(unique(unmatched)))} {?s} {.val {unique(unmatched)}} not found; dropping {n} row{cli::qty(n)}{?s}."
384+ )
385+ }
386+ df [[col ]] <- matched
387+ }
388+ df [stats :: complete.cases(df [pos_cols ]), , drop = FALSE ]
389+ }
390+
299391# R2R SVGs use font-size 7.1 Helvetica. The text x/y attributes give the
300392# left baseline of the character. These offsets shift to the visual center
301393# of the uppercase letter (approximately half character-width right, half
0 commit comments