b9f7469b125c83471a07002d953acbc50876a120
angie
  Mon Oct 24 17:07:27 2011 -0700
Code review advice from Tim about how to properly use ClosestToHome.
diff --git src/hg/hgTracks/vcfTrack.c src/hg/hgTracks/vcfTrack.c
index dd49567..e9956f0 100644
--- src/hg/hgTracks/vcfTrack.c
+++ src/hg/hgTracks/vcfTrack.c
@@ -6,91 +6,84 @@
 #include "errCatch.h"
 #include "hacTree.h"
 #include "hdb.h"
 #include "hgTracks.h"
 #include "pgSnp.h"
 #include "trashDir.h"
 #include "vcf.h"
 #include "vcfUi.h"
 #if (defined USE_TABIX && defined KNETFILE_HOOKS)
 #include "knetUdc.h"
 #include "udc.h"
 #endif//def USE_TABIX && KNETFILE_HOOKS
 
 #ifdef USE_TABIX
 
-static boolean getMinQual(struct trackDb *tdb, double *retMinQual, boolean compositeLevel)
+static boolean getMinQual(struct trackDb *tdb, double *retMinQual)
 /* Return TRUE and set retMinQual if cart contains minimum QUAL filter */
 {
-char cartVar[512];
-safef(cartVar, sizeof(cartVar), "%s."VCF_APPLY_MIN_QUAL_VAR, tdb->track);
-if (cartUsualBooleanClosestToHome(cart, tdb, compositeLevel,
+if (cartUsualBooleanClosestToHome(cart, tdb, FALSE,
 				  VCF_APPLY_MIN_QUAL_VAR, VCF_DEFAULT_APPLY_MIN_QUAL))
     {
     if (retMinQual != NULL)
-	*retMinQual = cartUsualDoubleClosestToHome(cart, tdb, compositeLevel, VCF_MIN_QUAL_VAR,
+	*retMinQual = cartUsualDoubleClosestToHome(cart, tdb, FALSE, VCF_MIN_QUAL_VAR,
 						   VCF_DEFAULT_MIN_QUAL);
     return TRUE;
     }
 return FALSE;
 }
 
 static boolean minQualFail(struct vcfRecord *record, double minQual)
 /* Return TRUE if record's QUAL column value is non-numeric or is less than minQual. */
 {
 if (isEmpty(record->qual) ||
     (record->qual[0] != '-' && !isdigit(record->qual[0])) ||
     atof(record->qual) < minQual)
     return TRUE;
 return FALSE;
 }
 
-static boolean getFilterValues(struct trackDb *tdb, struct slName **retValues,
-			       boolean compositeLevel)
+static boolean getFilterValues(struct trackDb *tdb, struct slName **retValues)
 /* Return TRUE and set retValues if cart contains FILTER column values to exclude */
 {
-char cartVar[512];
-safef(cartVar, sizeof(cartVar), "%s."VCF_EXCLUDE_FILTER_VAR, tdb->track);
-if (cartListVarExists(cart, cartVar))
+if (cartListVarExistsAnyLevel(cart, tdb, FALSE, VCF_EXCLUDE_FILTER_VAR))
     {
-    struct slName *selectedValues = cartOptionalSlNameList(cart, cartVar);
+    struct slName *selectedValues = cartOptionalSlNameListClosestToHome(cart, tdb, FALSE,
+									VCF_EXCLUDE_FILTER_VAR);
     if (retValues != NULL)
 	*retValues = selectedValues;
     return TRUE;
     }
 return FALSE;
 }
 
 static boolean filterColumnFail(struct vcfRecord *record, struct slName *filterValues)
 /* Return TRUE if record's FILTER column value(s) matches one of filterValues (from cart). */
 {
 int i;
 for (i = 0;  i < record->filterCount;  i++)
     if (slNameInList(filterValues, record->filters[i]))
 	return TRUE;
 return FALSE;
 }
 
-static boolean getMinFreq(struct trackDb *tdb, double *retMinFreq, boolean compositeLevel)
+static boolean getMinFreq(struct trackDb *tdb, double *retMinFreq)
 /* Return TRUE and set retMinFreq if cart contains nonzero minimum minor allele frequency. */
 {
-char cartVar[512];
-//#*** is there an ExistsClosestToHome?
-safef(cartVar, sizeof(cartVar), "%s."VCF_MIN_ALLELE_FREQ_VAR, tdb->track);
-if (cartVarExists(cart, cartVar))
+if (cartVarExistsAnyLevel(cart, tdb, FALSE, VCF_MIN_ALLELE_FREQ_VAR))
     {
-    double minFreq = cartUsualDoubleClosestToHome(cart, tdb, compositeLevel,
+    double minFreq = cartUsualDoubleClosestToHome(cart, tdb, FALSE,
 					    VCF_MIN_ALLELE_FREQ_VAR, VCF_DEFAULT_MIN_ALLELE_FREQ);
     if (minFreq > 0)
 	{
 	if (retMinFreq != NULL)
 	    *retMinFreq = minFreq;
 	return TRUE;
 	}
     }
 return FALSE;
 }
 
 static boolean minFreqFail(struct vcfRecord *record, double minFreq)
 /* Return TRUE if record's INFO include AF (alternate allele frequencies) or AC+AN
  * (alternate allele counts and total count of observed alleles) and the minor allele
  * frequency < minFreq -- or rather, major allele frequency > (1 - minFreq) because
@@ -138,37 +131,36 @@
 	    }
 	}
     }
 if (gotInfo)
     {
     double majorAlFreq = max(refFreq, maxAltFreq);
     if (majorAlFreq > (1.0 - minFreq))
 	return TRUE;
     }
 return FALSE;
 }
 
 static void filterRecords(struct vcfFile *vcff, struct trackDb *tdb)
 /* If a filter is specified in the cart, remove any records that don't pass filter. */
 {
-boolean compositeLevel = isNameAtCompositeLevel(tdb, tdb->track);
 double minQual = VCF_DEFAULT_MIN_QUAL;
 struct slName *filterValues = NULL;
 double minFreq = VCF_DEFAULT_MIN_ALLELE_FREQ;
-boolean gotQualFilter = getMinQual(tdb, &minQual, compositeLevel);
-boolean gotFilterFilter = getFilterValues(tdb, &filterValues, compositeLevel);
-boolean gotMinFreqFilter = getMinFreq(tdb, &minFreq, compositeLevel);
+boolean gotQualFilter = getMinQual(tdb, &minQual);
+boolean gotFilterFilter = getFilterValues(tdb, &filterValues);
+boolean gotMinFreqFilter = getMinFreq(tdb, &minFreq);
 if (! (gotQualFilter || gotFilterFilter || gotMinFreqFilter) )
     return;
 
 struct vcfRecord *rec, *nextRec, *newList = NULL;
 for (rec = vcff->records;  rec != NULL;  rec = nextRec)
     {
     nextRec = rec->next;
     if (! ((gotQualFilter && minQualFail(rec, minQual)) ||
 	   (gotFilterFilter && filterColumnFail(rec, filterValues)) ||
 	   (gotMinFreqFilter && minFreqFail(rec, minFreq)) ))
 	slAddHead(&newList, rec);
     }
 slReverse(&newList);
 vcff->records = newList;
 }
@@ -585,58 +577,54 @@
     }
 char *mouseoverText = gtSummaryString(rec);
 if (isCenter)
     {
     // Thick black lines to distinguish this variant:
     int yBot = yOff + tg->height - 2;
     hvGfxBox(hvg, x1-3, yOff, 3, tg->height, MG_BLACK);
     hvGfxBox(hvg, x2, yOff, 3, tg->height, MG_BLACK);
     hvGfxLine(hvg, x1-2, yOff, x2+2, yOff, MG_BLACK);
     hvGfxLine(hvg, x1-2, yBot, x2+2, yBot, MG_BLACK);
     // Special mouseover instructions:
     static struct dyString *dy = NULL;
     if (dy == NULL)
 	dy = dyStringNew(0);
     dyStringPrintf(dy, "%s   Haplotypes sorted on ", mouseoverText);
-    char cartVar[512];
-    safef(cartVar, sizeof(cartVar), "%s.centerVariantChrom", tg->tdb->track);
-    char *centerChrom = cartOptionalString(cart, cartVar);
+    char *centerChrom = cartOptionalStringClosestToHome(cart, tg->tdb, FALSE,
+							"centerVariantChrom");
     if (centerChrom == NULL || !sameString(chromName, centerChrom))
 	dyStringAppend(dy, "middle variant by default. ");
     else
 	dyStringAppend(dy, "this variant. ");
     dyStringAppend(dy, "To anchor sorting to a different variant, click on that variant and "
 		   "then click on the 'Use this variant' button below the variant name.");
     mouseoverText = dy->string;
     }
 mapBoxHgcOrHgGene(hvg, rec->chromStart, rec->chromEnd, x1, yOff, w, tg->height, tg->track,
 		  rec->name, mouseoverText, 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.centerVariantChrom", tg->tdb->track);
-char *centerChrom = cartOptionalString(cart, cartVar);
+char *centerChrom = cartOptionalStringClosestToHome(cart, tg->tdb, FALSE, "centerVariantChrom");
 if (centerChrom != NULL && sameString(chromName, centerChrom))
     {
-    safef(cartVar, sizeof(cartVar), "%s.centerVariantPos", tg->tdb->track);
-    int centerPos = cartInt(cart, cartVar);
+    int centerPos = cartUsualIntClosestToHome(cart, tg->tdb, FALSE, "centerVariantPos", -1);
     int winSize = seqEnd - seqStart;
     if (centerPos > (seqStart - winSize) && centerPos < (seqEnd + winSize))
 	{
 	int i;
 	struct vcfRecord *rec;
 	for (rec = records, i = 0;  rec != NULL;  rec = rec->next, i++)
 	    if (rec->chromStart >= centerPos)
 		return i;
 	return i-1;
 	}
     }
 return defaultIx;
 }
 
 /* Pixel y offset return type for recursive tree-drawing: */
@@ -804,32 +792,31 @@
 /* Ignore warnings from genotype parsing -- when there's one, there
  * are usually hundreds more just like it. */
 {
 }
 
 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;
 purple = hvGfxFindColorIx(hvg, 0x99, 0x00, 0xcc);
-boolean compositeLevel = isNameAtCompositeLevel(tg->tdb, tg->tdb->track);
-char *colorBy = cartUsualStringClosestToHome(cart, tg->tdb, compositeLevel,
+char *colorBy = cartUsualStringClosestToHome(cart, tg->tdb, FALSE,
 					     VCF_HAP_COLORBY_VAR, VCF_HAP_COLORBY_REFALT);
 boolean colorByRefAlt = sameString(colorBy, VCF_HAP_COLORBY_REFALT);
 pushWarnHandler(ignoreEm);
 struct vcfRecord *rec;
 for (rec = vcff->records;  rec != NULL;  rec = rec->next)
     vcfParseGenotypes(rec);
 popWarnHandler();
 unsigned short gtHapCount = 0;
 int ix, centerIx = getCenterVariantIx(tg, seqStart, seqEnd, vcff->records);
 struct hacTree *ht = NULL;
 unsigned short *gtHapOrder = clusterChroms(vcff, centerIx, &gtHapCount, &ht);
 struct vcfRecord *centerRec = NULL;
 for (rec = vcff->records, ix=0;  rec != NULL;  rec = rec->next, ix++)
     {
     if (ix == centerIx)
@@ -842,31 +829,35 @@
 // Draw as much of the tree as can fit in the left label area:
 drawTreeInLabelArea(ht, hvg, yOff, tg->height, gtHapCount, gtHapOrder);
 }
 
 static int vcfHapClusterTotalHeight(struct track *tg, enum trackVisibility vis)
 /* Return height of haplotype graph (2 * #samples * lineHeight);
  * 2 because we're assuming diploid genomes here, no XXY, tetraploid etc. */
 {
 // Should we make it single-height when on chrY?
 const struct vcfFile *vcff = tg->extraUiData;
 if (vcff->records == NULL)
     return 0;
 int ploidy = sameString(chromName, "chrY") ? 1 : 2;
 int simpleHeight = ploidy * vcff->genotypeCount * tg->lineHeight;
 int defaultHeight = min(simpleHeight, VCF_DEFAULT_HAP_HEIGHT);
-int cartHeight = cartOrTdbInt(cart, tg->tdb, VCF_HAP_HEIGHT_VAR, defaultHeight);
+char *tdbHeight = trackDbSettingOrDefault(tg->tdb, VCF_HAP_HEIGHT_VAR, NULL);
+if (isNotEmpty(tdbHeight))
+    defaultHeight = atoi(tdbHeight);
+int cartHeight = cartUsualIntClosestToHome(cart, tg->tdb, FALSE, VCF_HAP_HEIGHT_VAR,
+					   defaultHeight);
 tg->height = min(cartHeight+1, maximumTrackHeight(tg));
 return tg->height;
 }
 
 static char *vcfHapClusterTrackName(struct track *tg, void *item)
 /* If someone asks for itemName/mapItemName, just send name of track like wiggle. */
 {
 return tg->track;
 }
 
 static void vcfHapClusterOverloadMethods(struct track *tg, struct vcfFile *vcff)
 /* If we confirm at load time that we can draw a haplotype graph, use
  * this to overwrite the methods for the rest of execution: */
 {
 tg->heightPer = (tg->visibility == tvSquish) ? (tl.fontHeight/4) : (tl.fontHeight / 2);
@@ -889,32 +880,31 @@
 /* Figure out url or file name. */
 if (tg->parallelLoading)
     {
     /* do not use mysql uring parallel-fetch load */
     fileOrUrl = trackDbSetting(tg->tdb, "bigDataUrl");
     }
 else
     {
     // TODO: may need to handle per-chrom files like bam, maybe fold bamFileNameFromTable into this:
     struct sqlConnection *conn = hAllocConnTrack(database, tg->tdb);
     fileOrUrl = bbiNameFromSettingOrTable(tg->tdb, conn, tg->table);
     hFreeConn(&conn);
     }
 int vcfMaxErr = -1;
 struct vcfFile *vcff = NULL;
-boolean compositeLevel = isNameAtCompositeLevel(tg->tdb, tg->tdb->track);
-boolean hapClustEnabled = cartUsualBooleanClosestToHome(cart, tg->tdb, compositeLevel,
+boolean hapClustEnabled = cartUsualBooleanClosestToHome(cart, tg->tdb, FALSE,
 							VCF_HAP_ENABLED_VAR, TRUE);
 /* protect against temporary network error */
 struct errCatch *errCatch = errCatchNew();
 if (errCatchStart(errCatch))
     {
     vcff = vcfTabixFileMayOpen(fileOrUrl, chromName, winStart, winEnd, vcfMaxErr);
     if (vcff != NULL)
 	{
 	filterRecords(vcff, tg->tdb);
 	if (hapClustEnabled && vcff->genotypeCount > 1 && vcff->genotypeCount < 3000 &&
 	    (tg->visibility == tvPack || tg->visibility == tvSquish))
 	    vcfHapClusterOverloadMethods(tg, vcff);
 	else
 	    {
 	    tg->items = vcfFileToPgSnp(vcff, tg->tdb);