3c529301c2900f3b03ea89955426db75b9b631f9
angie
  Mon Feb 7 10:08:54 2011 -0800
Track #1684 (SNPs 132 (dbSNP)): Added coloring by allele frequency:items are shaded on a scale of red (rare) to blue (common), or black
when no frequencies were given.  Now I'm wondering if I should have
used a log scale, but this works so I'm checking it in.

diff --git src/hg/hgTracks/variation.c src/hg/hgTracks/variation.c
index 1754ee6..0ebe7ba 100644
--- src/hg/hgTracks/variation.c
+++ src/hg/hgTracks/variation.c
@@ -25,37 +25,30 @@
 static boolean snp125ClassFilterOn = FALSE;
 static boolean snp125MolTypeFilterOn = FALSE;
 static boolean snp125ValidFilterOn = FALSE;
 static boolean snp125FuncFilterOn = FALSE;
 static boolean snp132ExceptionFilterOn = FALSE;
 static boolean snp132BitfieldFilterOn = FALSE;
 
 static struct slName *snp125LocTypeFilter = NULL;
 static struct slName *snp125ClassFilter = NULL;
 static struct slName *snp125MolTypeFilter = NULL;
 static struct slName *snp125ValidFilter = NULL;
 static struct slName *snp125FuncFilter = NULL;
 static struct slName *snp132ExceptionFilter = NULL;
 static struct slName *snp132BitfieldFilter = NULL;
 
-void filterSnpMapItems(struct track *tg, boolean (*filter)
-		       (struct track *tg, void *item))
-/* Filter out items from track->itemList. */
-{
-filterSnpItems(tg, filter);
-}
-
 void filterSnpItems(struct track *tg, boolean (*filter)
 		    (struct track *tg, void *item))
 /* Filter out items from track->itemList. */
 {
 struct slList *newList = NULL, *el, *next;
 
 for (el = tg->items; el != NULL; el = next)
     {
     next = el->next;
     if (filter(tg, el))
  	slAddHead(&newList, el);
     }
 slReverse(&newList);
 tg->items = newList;
 }
@@ -356,30 +349,41 @@
 	snp125MolTypeFilterItem(el) &&
 	snp125ClassFilterItem(el) &&
 	snp125ValidFilterItem(el) &&
 	snp125FuncFilterItem(el) &&
 	(version >= 128 || snp125LocTypeFilterItem(el)) &&
 	(version < 132 ||
 	 (snp132MinSubmittersFilterItem(el) &&
 	  snp132ExceptionFilterItem(el) &&
 	  snp132BitfieldFilterItem(el))))
         slAddHead(&newList, el);
     }
 slReverse(&newList);
 tg->items = newList;
 }
 
+struct orthoBed
+/* Abbreviated version of orthoAlleles: bed4 plus chimp allele */
+    {
+    struct orthoBed *next;       /* Next in singly linked list. */
+    char            *chrom;      /* Human chromosome or FPC contig */
+    unsigned         chromStart; /* Start position in chromosome */
+    unsigned         chromEnd;   /* End position in chromosome */
+    char            *name;       /* Name of item */
+    char            *chimp;      /* Chimp allele */
+    };
+
 struct orthoBed *orthoBedLoad(char **row)
 /* Load a bed from row fetched with select * from bed
  * from database.  Dispose of this with bedFree(). */
 /* This should be moved to kent/src/hg/lib. */
 {
 struct orthoBed *ret;
 if (sameString(row[10], "?"))
     return NULL;
 AllocVar(ret);
 ret->chrom      = cloneString(row[0]);
 ret->chromStart = sqlUnsigned(row[1]);
 ret->chromEnd   = sqlUnsigned(row[2]);
 ret->name       = cloneString(row[3]);
 ret->chimp      = cloneString(row[10]);
 return ret;
@@ -465,35 +469,78 @@
 	continue;
 	}
     /* update the snp->name with the ortho data */
     dyStringPrintf(extra, "%s %s>%s", snpItem->name, orthoItem->chimp, snpItem->observed);
     snpItem->name = cloneString(extra->string);
     dyStringClear(extra);
     /* increment the list pointers */
     snpItem = snpItem->next;
     orthoItem = orthoItem->next;
     }
 freeDyString(&extra);
 sqlFreeResult(&sr);
 hFreeConn(&conn);
 }
 
-Color snp125Color(struct track *tg, void *item, struct hvGfx *hvg)
-/* Return color of snp track item -- which we stash in the weight column (only the bed4
- * fields of snp are used at draw time). */
+static float snp132MajorAlleleFreq(const struct snp132Ext *snp)
+/* Some SNPs have >2 alleles, so minor allele frequency is harder to define.
+ * So instead, I'm using major allele frequency -- (1 - major) can be a proxy for minor. */
 {
-struct snp125 *snp = item;
+float majorAlF = 0.0;
+int i;
+for (i = 0;  i < snp->alleleFreqCount;  i++)
+    if (snp->alleleFreqs[i] > majorAlF)
+	majorAlF = snp->alleleFreqs[i];
+return majorAlF;
+}
+
+static Color snp132ColorByAlleleFreq(struct snp132Ext *snp, struct hvGfx *hvg)
+/* If snp has allele freq data, return a shade from red (rare) to blue (common);
+ * otherwise return black. */
+{
+static boolean colorsInited = FALSE;
+static Color redToBlue[EXPR_DATA_SHADES];
+static struct rgbColor red = {255, 0, 0};
+static struct rgbColor blue = {0, 0, 255};
+if (!colorsInited)
+    hvGfxMakeColorGradient(hvg, &red, &blue, EXPR_DATA_SHADES, redToBlue);
+if (snp->alleleFreqCount > 0)
+    {
+    float majorAlF = snp132MajorAlleleFreq(snp);
+    // >2 common alleles (e.g. at VNTR sites) can cause low major allele freq;
+    // cap at 0.5 to avoid overflow in the shade calculation.
+    if (majorAlF < 0.5)
+	majorAlF = 0.5;
+    if (majorAlF > 1.0)
+	majorAlF = 1.0;
+    // Shade on a scale of 100% (red) to 50% (blue):
+    int shadeIndex = (int)((1.0 - 2.0*(majorAlF - 0.5)) * (EXPR_DATA_SHADES-1));
+    return redToBlue[shadeIndex];
+    }
+return MG_BLACK;
+}
+
+Color snp125Color(struct track *tg, void *item, struct hvGfx *hvg)
+/* Return color of snp track item -- stashed in the weight column for set/enum 
+ * attributes that were used for sorting at draw time.  Allele frequency shading
+ * must be done at draw time because it uses hvg.  Aside from allele frequencies
+ * and overloaded weight, only the bed4 fields of snp are used at draw time). */
+{
+struct snp132Ext *snp = item;
+if (snp125ColorSource == snp125ColorSourceAlleleFreq)
+    return snp132ColorByAlleleFreq(snp, hvg);
+else
 return (Color)(snp->weight);
 }
 
 int snp125ColorCmpRaw(const Color ca, const char *aName, const Color cb, const char *bName)
 /* Compare to sort based on color -- black first, red last.  This is not
  * a slSort Cmp function, just a comparator of the values. */
 {
 /* order is important here */
 if (ca==cb)
     return 0;
 if (ca==MG_RED)
     return 1;
 if (cb==MG_RED)
     return -1;
 if (ca==MG_GREEN)
@@ -518,36 +565,31 @@
 
 int snp125ColorCmp(const void *va, const void *vb)
 /* Compare to sort based on color -- black first, red last */
 {
 const struct snp125 *a = *((struct snp125 **)va);
 const struct snp125 *b = *((struct snp125 **)vb);
 const Color ca = (Color)(a->weight);
 const Color cb = (Color)(b->weight);
 
 return snp125ColorCmpRaw(ca, a->name, cb, b->name);
 }
 
 int snp125ColorCmpDesc(const void *va, const void *vb)
 /* Compare to sort based on color -- red first, black last */
 {
-const struct snp125 *a = *((struct snp125 **)va);
-const struct snp125 *b = *((struct snp125 **)vb);
-const Color ca = (Color)(a->weight);
-const Color cb = (Color)(b->weight);
-
-return snp125ColorCmpRaw(cb, b->name, ca, a->name);
+return snp125ColorCmp(vb, va);
 }
 
 static enum snp125Color *snp125ColorsFromCart(char *track, char *attribute,
 				 char *vars[], boolean varsAreOld, char *defaults[], int varCount)
 /* Look up attribute colors in cart using both old and new cart var names where applicable. */
 {
 enum snp125Color *cartColors = NULL;
 AllocArray(cartColors, varCount);
 int i;
 for (i=0; i < varCount; i++)
     {
     char cartVar[512];
     safef(cartVar, sizeof(cartVar), "%s.%s%s", track, attribute,
 	  (varsAreOld ? snp125OldColorVarToNew(vars[i], attribute) : vars[i]));
     char *defaultCol = defaults[i];
@@ -575,37 +617,31 @@
     defaultMaxWeight = atoi(setting);
 snp125WeightCutoff = cartUsualInt(cart, cartVar,
 			     // Check old cart var name and tdb default:
 			     cartUsualInt(cart, "snp125WeightCutoff", defaultMaxWeight));
 safef(cartVar, sizeof(cartVar), "%s.minSubmitters", track);
 snp132MinSubmitters = cartUsualInt(cart, cartVar, SNP132_DEFAULT_MIN_SUBMITTERS);
 
 snp125MolTypeFilter = snp125FilterFromCart(cart, track, "molType", &snp125MolTypeFilterOn);
 snp125ClassFilter = snp125FilterFromCart(cart, track, "class", &snp125ClassFilterOn);
 snp125ValidFilter = snp125FilterFromCart(cart, track, "valid", &snp125ValidFilterOn);
 snp125FuncFilter = snp125FilterFromCart(cart, track, "func", &snp125FuncFilterOn);
 snp125LocTypeFilter = snp125FilterFromCart(cart, track, "locType", &snp125LocTypeFilterOn);
 snp132ExceptionFilter = snp125FilterFromCart(cart, track, "exceptions", &snp132ExceptionFilterOn);
 snp132BitfieldFilter = snp125FilterFromCart(cart, track, "bitfields", &snp132BitfieldFilterOn);
 
-safef(cartVar, sizeof(cartVar), "%s.colorSource", track);
-char *snp125ColorSourceDefault = snp125ColorSourceLabels[SNP125_DEFAULT_COLOR_SOURCE];
-char *colorSourceCart = cartUsualString(cart, cartVar,
-					cartUsualString(cart, snp125ColorSourceOldVar,
-							snp125ColorSourceDefault));
-snp125ColorSource = stringArrayIx(colorSourceCart, snp125ColorSourceLabels,
-				  snp125ColorSourceArraySize);
+snp125ColorSource = snp125ColorSourceFromCart(cart, tdb);
 snp125MolTypeCart = snp125ColorsFromCart(track, "molType", snp125MolTypeOldColorVars, TRUE,
 					 snp125MolTypeDefault, snp125MolTypeArraySize);
 snp125ClassCart = snp125ColorsFromCart(track, "class", snp125ClassOldColorVars, TRUE,
 				       snp125ClassDefault, snp125ClassArraySize);
 snp125ValidCart = snp125ColorsFromCart(track, "valid", snp125ValidOldColorVars, TRUE,
 				       snp125ValidDefault, snp125ValidArraySize);
 snp125LocTypeCart = snp125ColorsFromCart(track, "locType", snp125LocTypeOldColorVars, TRUE,
 					 snp125LocTypeDefault, snp125LocTypeArraySize);
 snp132ExceptionsCart = snp125ColorsFromCart(track, "exception", snp132ExceptionVarName, FALSE,
 					    snp132ExceptionDefault, snp132ExceptionArraySize);
 snp132BitfieldsCart = snp125ColorsFromCart(track, "bitfield", snp132BitfieldVarName, FALSE,
 					   snp132BitfieldDefault, snp132BitfieldArraySize);
 
 snp125FuncCartColorHash = hashNew(0);
 snp125FuncCartNameHash = hashNew(0);
@@ -654,31 +690,33 @@
 	break;
     case snp125ColorBlue:
 	return MG_BLUE;
 	break;
     case snp125ColorGray:
 	return MG_GRAY;
 	break;
     case snp125ColorBlack:
     default:
 	return MG_BLACK;
 	break;
     }
 }
 
 static void snp125ColorItems(struct track *tg, int version)
-/* Use cart settings and snp properties to assign a color to snp -- and stash it in snp->weight. */
+/* Use cart settings and snp properties to assign a color to snp -- and stash it in snp->weight.
+ * Note: we can't do the allele frequency shading here because that uses hvg for the shades,
+ * and hvg isn't passed in until draw time. */
 {
 struct snp132Ext *snp;
 for (snp = tg->items;  snp != NULL;  snp = snp->next)
     {
     enum snp125Color color = snp125ColorBlack;
     int valIx;
     char *words[128];
     int wordCount, i;
     char buf[4096];
     switch (snp125ColorSource)
 	{
 	case snp125ColorSourceMolType:
 	    valIx = stringArrayIx(snp->molType, snp125MolTypeDataName, snp125MolTypeArraySize);
 	    if (valIx < 0)
 		valIx = 0;
@@ -740,103 +778,147 @@
 	    else
 		{
 		safecpy(buf, sizeof(buf), snp->bitfields);
 		wordCount = chopCommas(buf, words);
 		for (i = 0;  i < wordCount;  i++)
 		    {
 		    valIx = stringArrayIx(words[i], snp132BitfieldDataName, snp132BitfieldArraySize);
 		    enum snp125Color wordColor = snp132BitfieldsCart[valIx];
 		    if (snp125ColorCmpRaw(snp125ColorToMg(wordColor), "wordColor",
 					  snp125ColorToMg(color), "color") > 0)
 			color = wordColor;
 		    }
 		}
 	    }
 	    break;
+	case snp125ColorSourceAlleleFreq:
 	default:
 	    color = snp125ColorBlack;
 	    break;
 	}
     snp->weight = snp125ColorToMg(color);
     }
 }
 
 static void loadSnp125Basic(struct track *tg, int version, void *(loadFunction)(char **))
 /* load snp125 or snp132Ext items from table */
 {
 struct sqlConnection *conn = hAllocConn(database);
 struct snp132Ext *itemList = NULL;
 int rowOffset = 0;
 char **row = NULL;
 struct sqlResult *sr = hRangeQuery(conn, tg->table, chromName, winStart, winEnd, NULL, &rowOffset);
 while ((row = sqlNextRow(sr)) != NULL)
     {
     struct slList *snp = loadFunction(row + rowOffset);
     slAddHead(&itemList, snp);
     }
 sqlFreeResult(&sr);
 hFreeConn(&conn);
 tg->items = itemList;
 }
 
+static int bedCmpStartName(const void *va, const void *vb)
+/* Compare two bed4's by chromStart (chrom assumed to be same) and name. */
+{
+const struct bed *a = *((struct bed **)va);
+const struct bed *b = *((struct bed **)vb);
+int diff = a->chromStart - b->chromStart;
+if (diff == 0)
+    diff = strcmp(a->name, b->name);
+return diff;
+}
+
+static int snp132AlFreqCmp(const void *va, const void *vb)
+/* Compare two SNPs by allele frequency: (no info/)rare first, common last. */
+{
+const struct snp132Ext *a = *((struct snp132Ext **)va);
+const struct snp132Ext *b = *((struct snp132Ext **)vb);
+if (a->alleleFreqCount == 0 && b->alleleFreqCount == 0)
+    return bedCmpStartName(va, vb);
+else if (a->alleleFreqCount == 0 && b->alleleFreqCount != 0)
+    return -1;
+else if (a->alleleFreqCount == 0 && b->alleleFreqCount != 0)
+    return 1;
+else
+    {
+    float majorAlFA = snp132MajorAlleleFreq(a);
+    float majorAlFB = snp132MajorAlleleFreq(b);
+    if (majorAlFA > majorAlFB)
+	return -1;
+    else if (majorAlFA < majorAlFB)
+	return 1;
+    else
+	return bedCmpStartName(va, vb);
+    }
+}
+
+static int snp132AlFreqCmpDesc(const void *va, const void *vb)
+/* Compare two SNPs by allele frequency: common first, (no info/)rare last. */
+{
+return snp132AlFreqCmp(vb, va);
+}
+
 void loadSnp125(struct track *tg)
 /* load snps from table, ortho alleles from snpXXXOrthoXXX table, filter and color. */
 {
 int version = snpVersion(tg->table);
 if (version >= 132)
     loadSnp125Basic(tg, version, (void *)(snp132ExtLoad));
 else if (version >= 125)
     loadSnp125Basic(tg, version, (void *)(snp125Load));
 else
     errAbort("How was loadSnp125 called on version < 125? (%d)", version);
 
 snp125SetupFiltersAndColorsFromCart(tg->tdb);
 filterSnp125Items(tg, version);
 snp125ColorItems(tg, version);
 
+// If in dense or squish mode, sort items by color or allele frequency:
+boolean sortByAF = (snp125ColorSource == snp125ColorSourceAlleleFreq);
 if (tg->visibility == tvDense)
-    slSort(&tg->items, snp125ColorCmp);
+    slSort(&tg->items, sortByAF ? snp132AlFreqCmp : snp125ColorCmp);
 else if (tg->visibility == tvSquish)
-    slSort(&tg->items, snp125ColorCmpDesc);
+    slSort(&tg->items, sortByAF ? snp132AlFreqCmpDesc : snp125ColorCmpDesc);
 else
     {
     slSort(&tg->items, snpOrthoCmp);
     setSnp125ExtendedNameExtra(tg);
     enum trackVisibility newVis = limitVisibility(tg);
     if (newVis == tvDense)
-	slSort(&tg->items, snp125ColorCmp);
+	slSort(&tg->items, sortByAF ? snp132AlFreqCmp : snp125ColorCmp);
     else if (newVis == tvSquish)
-	slSort(&tg->items, snp125ColorCmpDesc);
+	slSort(&tg->items, sortByAF ? snp132AlFreqCmpDesc : snp125ColorCmpDesc);
     }
 }
 
 void loadSnpMap(struct track *tg)
 /* Load up snpMap from database table to track items. */
 {
 int  snpMapSource, snpMapType;
 
 for (snpMapSource=0; snpMapSource<snpMapSourceCartSize; snpMapSource++)
     snpMapSourceCart[snpMapSource] = cartUsualString(cart,
        snpMapSourceStrings[snpMapSource], snpMapSourceDefault[snpMapSource]);
 for (snpMapType=0; snpMapType<snpMapTypeCartSize; snpMapType++)
     snpMapTypeCart[snpMapType] = cartUsualString(cart,
        snpMapTypeStrings[snpMapType], snpMapTypeDefault[snpMapType]);
 bedLoadItem(tg, "snpMap", (ItemLoader)snpMapLoad);
 if (!startsWith("hg",database))
     return;
-filterSnpMapItems(tg, snpMapSourceFilterItem);
-filterSnpMapItems(tg, snpMapTypeFilterItem);
+filterSnpItems(tg, snpMapSourceFilterItem);
+filterSnpItems(tg, snpMapTypeFilterItem);
 }
 
 void loadSnp(struct track *tg)
 /* Load up snp from database table to track items. */
 {
 int  i = 0;
 
 snpAvHetCutoff = atof(cartUsualString(cart, "snpAvHetCutoff", "0.0"));
 
 for (i=0; i < snpSourceCartSize;  i++)
     snpSourceCart[i]  = cartUsualString(cart, snpSourceStrings[i],  snpSourceDefault[i]);
 for (i=0; i < snpMolTypeCartSize; i++)
     snpMolTypeCart[i] = cartUsualString(cart, snpMolTypeStrings[i], snpMolTypeDefault[i]);
 for (i=0; i < snpClassCartSize;   i++)
     snpClassCart[i]   = cartUsualString(cart, snpClassStrings[i],   snpClassDefault[i]);
@@ -1007,31 +1089,31 @@
 	     tg->track, tg->mapItemName(tg, s), NULL);
 }
 
 void snp125DrawItemAt(struct track *tg, void *item, struct hvGfx *hvg, int xOff, int y,
 	double scale, MgFont *font, Color color, enum trackVisibility vis)
 /* Draw a single snp125 item at position. */
 {
 struct snp125 *s = item;
 int heightPer = tg->heightPer;
 int x1 = round((double)((int)s->chromStart-winStart)*scale) + xOff;
 int x2 = round((double)((int)s->chromEnd-winStart)*scale) + xOff;
 int w = x2-x1;
 
 if (w < 1)
     w = 1;
-hvGfxBox(hvg, x1, y, w, heightPer, (Color)(s->weight));
+hvGfxBox(hvg, x1, y, w, heightPer, color);
 /* Clip here so that text will tend to be more visible... */
 if (tg->drawName && vis != tvSquish)
     mapBoxHc(hvg, s->chromStart, s->chromEnd, x1, y, w, heightPer,
 	     tg->track, tg->mapItemName(tg, s), NULL);
 }
 
 static void snpMapDrawItems(struct track *tg, int seqStart, int seqEnd,
         struct hvGfx *hvg, int xOff, int yOff, int width,
         MgFont *font, Color color, enum trackVisibility vis)
 /* Draw snpMap items. */
 {
 double scale = scaleForPixels(width);
 int lineHeight = tg->lineHeight;
 int heightPer = tg->heightPer;
 int w, y;
@@ -1095,30 +1177,31 @@
 	    y += lineHeight;
         }
     }
 }
 
 static void snpDrawItems(struct track *tg, int seqStart, int seqEnd,
         struct hvGfx *hvg, int xOff, int yOff, int width,
         MgFont *font, Color color, enum trackVisibility vis)
 /* Draw snp items. */
 {
 double scale = scaleForPixels(width);
 int lineHeight = tg->lineHeight;
 int heightPer = tg->heightPer;
 int y, w;
 boolean withLabels = (withLeftLabels && vis == tvPack && !tg->drawName);
+snp125ColorSource = snp125ColorSourceFromCart(cart, tg->tdb);
 
 if (!tg->drawItemAt)
     errAbort("missing drawItemAt in track %s", tg->track);
 if (vis == tvPack || vis == tvSquish)
     {
     struct spaceSaver *ss = tg->ss;
     struct spaceNode *sn = NULL;
     hvGfxSetClip(hvg, insideX, yOff, insideWidth, tg->height);
     for (sn = ss->nodeList; sn != NULL; sn = sn->next)
         {
         struct slList *item = sn->val;
         int s = tg->itemStart(tg, item);
         int e = tg->itemEnd(tg, item);
         int x1 = round((s - winStart)*scale) + xOff;
         int x2 = round((e - winStart)*scale) + xOff;