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);