4aa77873cb618e254b2f90d091d7bdef844947c2
max
  Wed Jul 8 04:28:42 2026 -0700
Let assembly hubs assign genetic codes per sequence for amino acid display

Adds a "codonTable" genomes.txt setting, e.g. "codonTable default=1
NC_017929.1=13", so an assembly hub can pick the NCBI translation table used to
show amino acids for each sequence.  New hGeneticCodeForChrom(db, chrom) in
hdb.c resolves the code (per-db cached), falling back to the previous behavior:
chrM/chrMT use the vertebrate mitochondrial code, everything else the standard
code.

Wired into the two browser display paths, which both go through cds.c's
baseColorLookupCodon: the base position track three-frame translation and
codon-colored annotation tracks such as gene predictions (also PSL/BAM).  Also
used for the hgc SNP amino acid details, and genePredTranslate gains a db
parameter (genePredToProt gains an optional -db flag) so command-line
translation can honor the same setting.  Documented in assemblyHubHelp.html.
refs #16550

diff --git src/hg/lib/hdb.c src/hg/lib/hdb.c
index eb891021d51..b182f47dc05 100644
--- src/hg/lib/hdb.c
+++ src/hg/lib/hdb.c
@@ -140,30 +140,108 @@
 if ((row = sqlNextRow(sr)) != NULL)
     {
     ci = chromInfoLoad(row);
     }
 sqlFreeResult(&sr);
 hFreeConn(&conn);
 return ci;
 }
 
 boolean isMito(char *chrom)
 /* Return True if chrom is chrM or chrMT */
 {
 return sameString(chrom, "chrM") || sameString(chrom, "chrMT");
 }
 
+struct dbCodonTable
+/* Per-database parse of an assembly hub "codonTable" setting. */
+    {
+    struct geneticCode *defaultCode;   /* From "default=N", or NULL if unset. */
+    struct hash *seqToCode;            /* seqName -> struct geneticCode *. */
+    };
+
+static struct dbCodonTable *codonTableForDb(char *db)
+/* Parse (and cache per db) the assembly hub "codonTable" setting, e.g.
+ * "codonTable default=1 chrM=2".  Returns an entry with an empty seqToCode
+ * (and NULL defaultCode) for databases with no such setting. */
+{
+static struct hash *cache = NULL;
+if (cache == NULL)
+    cache = hashNew(0);
+char *key = (db != NULL) ? db : "";
+struct dbCodonTable *ct = hashFindVal(cache, key);
+if (ct != NULL)
+    return ct;
+
+AllocVar(ct);
+ct->seqToCode = hashNew(0);
+char *setting = NULL;
+if (db != NULL && trackHubDatabase(db))
+    {
+    struct trackHubGenome *genome = trackHubGetGenome(db);
+    if (genome != NULL)
+        setting = hashFindVal(genome->settingsHash, "codonTable");
+    }
+if (setting != NULL)
+    {
+    char *dupe = cloneString(setting);
+    char *words[512];
+    int wordCount = chopByWhite(dupe, words, ArraySize(words));
+    int i;
+    for (i = 0;  i < wordCount;  ++i)
+        {
+        char *eq = strchr(words[i], '=');
+        if (eq == NULL)
+            {
+            warn("codonTable setting for %s: ignoring \"%s\", expected seqName=id", db, words[i]);
+            continue;
+            }
+        *eq = '\0';
+        char *seq = words[i];
+        struct geneticCode *code = geneticCodeForId(atoi(eq+1));
+        if (code == NULL)
+            {
+            warn("codonTable setting for %s: ignoring \"%s\", unknown genetic code id", db, seq);
+            continue;
+            }
+        if (sameString(seq, "default"))
+            ct->defaultCode = code;
+        else
+            hashAdd(ct->seqToCode, seq, code);
+        }
+    freez(&dupe);
+    }
+hashAdd(cache, key, ct);
+return ct;
+}
+
+struct geneticCode *hGeneticCodeForChrom(char *db, char *chrom)
+/* Return the genetic code (translation table) to use for chrom in db.  An
+ * assembly hub may assign codes per sequence with a genomes.txt line like
+ * "codonTable default=1 chrM=2".  For backward compatibility, when no such
+ * assignment applies, chrM/chrMT use the vertebrate mitochondrial code and all
+ * other sequences use the standard code.  Never returns NULL. */
+{
+struct dbCodonTable *ct = codonTableForDb(db);
+struct geneticCode *code = hashFindVal(ct->seqToCode, chrom);
+if (code == NULL)
+    code = ct->defaultCode;
+if (code == NULL)
+    code = geneticCodeForId(isMito(chrom) ? 2 : 1);
+return code;
+}
+
 struct chromInfo *hGetChromInfo(char *db, char *chrom)
 /* Get chromInfo for named chromosome (case-insens.) from db.
  * Return NULL if no such chrom. */
 /* Cache results, but build up the hash incrementally instead of in one slurp
  * from chromInfo because that takes a *long* time for scaffold-based dbs and
  * is usually not necessary. */
 {
 static struct hash *dbToInfo = NULL;
 struct hash *infoHash = NULL;
 struct hashEl *dHel = NULL;
 struct chromInfo *ci = NULL;
 char upcName[HDB_MAX_CHROM_STRING];
 if (strlen(chrom) >= HDB_MAX_CHROM_STRING)
     return NULL;
 safef(upcName, sizeof(upcName), "%s", chrom);