820ebcf85a344a03cf2279b6ceee42a25514f4a3
angie
  Mon May 23 09:41:50 2011 -0700
Feature #3711 (VCF haplotype clustering): User can select which variantis used as the center (for center-weighted clustering) by clicking on a
link in its details page.

diff --git src/hg/hgTracks/vcfTrack.c src/hg/hgTracks/vcfTrack.c
index 036741b..420ade8 100644
--- src/hg/hgTracks/vcfTrack.c
+++ src/hg/hgTracks/vcfTrack.c
@@ -316,35 +316,30 @@
 {
 if (grayUnphasedHet && !gt->isPhased && gt->hapIxA != gt->hapIxB)
     return shadesOfGray[5];
 int alIx = hapIx ? gt->hapIxB : gt->hapIxA;
 return alIx ? MG_RED : MG_BLUE;
 }
 
 
 INLINE int drawOneHap(struct vcfGenotype *gt, int hapIx,
 		      char *ref, char *altAlleles[], int altCount,
 		      struct hvGfx *hvg, int x1, int y, int w, int itemHeight, int lineHeight)
 /* Draw a base-colored box for genotype[hapIx].  Return the new y offset. */
 {
 Color color = colorHapByRefAlt ? colorFromRefAlt(gt, hapIx, TRUE) :
 				 colorFromGt(gt, hapIx, ref, altAlleles, altCount, TRUE);
-if (w == 1)
-    {
-    x1--;
-    w = 3;
-    }
 hvGfxBox(hvg, x1, y, w, itemHeight+1, color);
 y += itemHeight+1;
 return y;
 }
 
 INLINE char *gtSummaryString(struct vcfRecord *rec, char **altAlleles, int altCount)
 // Make pgSnp-like mouseover text, but with genotype counts instead of allele counts.
 // NOTE 1: Returned string is statically allocated, don't free it!
 // NOTE 2: if revCmplDisp is set, this reverse-complements rec->ref and altAlleles!
 {
 static struct dyString *dy = NULL;
 if (dy == NULL)
     dy = dyStringNew(0);
 dyStringClear(dy);
 const struct vcfFile *vcff = rec->file;
@@ -380,64 +375,99 @@
 static void drawOneRec(struct vcfRecord *rec, unsigned short *gtHapOrder, int gtHapEnd,
 		       struct track *tg, struct hvGfx *hvg, int xOff, int yOff, int width)
 /* Draw a stack of genotype bars for this record */
 {
 static struct dyString *tmp = NULL;
 if (tmp == NULL)
     tmp = dyStringNew(0);
 char *altAlleles[256];
 int altCount;
 const int lineHeight = tg->lineHeight;
 const int itemHeight = tg->heightPer;
 const double scale = scaleForPixels(width);
 int x1 = round((double)(rec->chromStart-winStart)*scale) + xOff;
 int x2 = round((double)(rec->chromEnd-winStart)*scale) + xOff;
 int w = x2-x1;
-if (w < 1)
-    w = 1;
+if (w <= 1)
+    {
+    x1--;
+    w = 3;
+    }
 int y = yOff;
 dyStringClear(tmp);
 dyStringAppend(tmp, rec->alt);
 altCount = chopCommas(tmp->string, altAlleles);
 int gtHapOrderIx;
 for (gtHapOrderIx = 0;  gtHapOrderIx < gtHapEnd;  gtHapOrderIx++)
     {
     int gtHapIx = gtHapOrder[gtHapOrderIx];
     int hapIx = gtHapIx & 1;
     int gtIx = gtHapIx >>1;
     struct vcfGenotype *gt = &(rec->genotypes[gtIx]);
     y = drawOneHap(gt, hapIx, rec->ref, altAlleles, altCount,
 		   hvg, x1, y, w, itemHeight, lineHeight);
     }
 mapBoxHgcOrHgGene(hvg, rec->chromStart, rec->chromEnd, x1, yOff, w, tg->height, tg->track,
 		  rec->name, gtSummaryString(rec, altAlleles, altCount),
 		  NULL, TRUE, NULL);
 }
 
+static int getCenterVariantIx(struct track *tg, int seqStart, int seqEnd,
+			      struct vcfRecord *records)
+// If the user hasn't specified a local variant/position to use as center,
+// just use the median variant in window.
+{
+int defaultIx = (slCount(records)-1) / 2;
+char cartVar[512];
+safef(cartVar, sizeof(cartVar), "%s.centerVariantPos", tg->tdb->track);
+char *centerPos = cartOptionalString(cart, cartVar);
+if (centerPos != NULL)
+    {
+    char *words[3];
+    int wordCount = chopByChar(cloneString(centerPos), ':', words, sizeof(words));
+    if (wordCount != 2)
+	errAbort("Cart variable %s format error: expected 'chrom:pos', got %s",
+		 cartVar, centerPos);
+    if (sameString(chromName, words[0]))
+	{
+	int pos = sqlUnsigned(words[1]);
+	int winSize = seqEnd - seqStart;
+	if (pos > (seqStart - winSize) && pos < (seqEnd + winSize))
+	    {
+	    int i;
+	    struct vcfRecord *rec;
+	    for (rec = records, i = 0;  rec != NULL;  rec = rec->next, i++)
+		if (rec->chromStart >= pos)
+		    return i;
+	    return i-1;
+	    }
+	}
+    }
+return defaultIx;
+}
+
 static void vcfHapClusterDraw(struct track *tg, int seqStart, int seqEnd,
 			      struct hvGfx *hvg, int xOff, int yOff, int width,
 			      MgFont *font, Color color, enum trackVisibility vis)
 /* Split samples' chromosomes (haplotypes), cluster them by center-weighted
  * alpha similarity, and draw in the order determined by clustering. */
 {
 const struct vcfFile *vcff = tg->extraUiData;
 if (vcff->records == NULL)
     return;
 unsigned short gtHapEnd = 0;
-// Use the median variant in the window as the center; would be even nicer to allow
-// the user to choose a variant (or position) to use as center:
-int ix, centerIx = (slCount(vcff->records)-1) / 2;
+int ix, centerIx = getCenterVariantIx(tg, seqStart, seqEnd, vcff->records);
 unsigned short *gtHapOrder = clusterChroms(vcff, centerIx, &gtHapEnd);
 struct vcfRecord *rec, *centerRec = NULL;
 for (rec = vcff->records, ix=0;  rec != NULL;  rec = rec->next, ix++)
     {
     drawOneRec(rec, gtHapOrder, gtHapEnd, tg, hvg, xOff, yOff, width);
     if (ix == centerIx)
 	centerRec = rec;
     }
 // Draw the center rec on top, outlined with black lines, to make sure it is very visible:
 drawOneRec(centerRec, gtHapOrder, gtHapEnd, tg, hvg, xOff, yOff, width);
 const double scale = scaleForPixels(width);
 int x1 = round((double)(centerRec->chromStart-winStart)*scale) + xOff;
 int x2 = round((double)(centerRec->chromEnd-winStart)*scale) + xOff;
 int yBot = yOff + tg->height - 2;
 hvGfxBox(hvg, x1-4, yOff, 3, tg->height, color);