5be304c4bdd41bd4452a4b9f1b8fcf37930079e4
braney
  Fri Feb 3 11:09:59 2017 -0800
allow arbitrary fields with a bigBed file to be used for labels.  #18782

diff --git src/hg/hgTracks/bedTrack.c src/hg/hgTracks/bedTrack.c
index 5fb6a7e..27c3256 100644
--- src/hg/hgTracks/bedTrack.c
+++ src/hg/hgTracks/bedTrack.c
@@ -20,30 +20,77 @@
 /* Load first six fields of bed.
  * Add ~seq1~seq2 to end of name
  * Then remove the sequence to extra field when we convert to linkedFeature.
  * Assumes seq1 and seq2 are in row[6] and row[7], as they would be with a
  * pairedTagAlign type (hg/lib/encode/pairedTagAlign.as). It would be good to be
  * able to check these columns exist but we dont have the sqlResult here. */
 {
 char buf[1024];
 struct bed *ret = bedLoad6(row);
 safef(buf, sizeof(buf), "%s%c%s%c%s", ret->name, SEQ_DELIM, row[6], SEQ_DELIM, row[7]);
 freez(&(ret->name));
 ret->name = cloneString(buf);
 return ret;
 }
 
+static void calculateLabelFields(struct track *track)
+/* Figure out which fields are available to label a bigBed track. */
+{
+char *labelFields = trackDbSettingClosestToHome(track->tdb, "labelFields");
+
+if (labelFields == NULL)
+    {
+    // if there is a name, use it by default, otherwise no label by default 
+    if (track->bedSize > 3)
+        slAddHead(&track->labelColumns, slIntNew(3));
+    }
+else
+    {
+    char cartVar[1024];
+    // what has the user said to use as a label
+    safef(cartVar, sizeof cartVar, "%s.label", track->tdb->track);
+    struct hashEl *labelEl = cartFindPrefix(cart, cartVar);
+    struct hash *labelHash = newHash(5);
+    struct slName *thisLabel, *labelIds = slNameListFromComma(labelFields);
+
+    // fill hash with fields that should be used for labels
+    if (labelEl == NULL) 
+        hashAdd(labelHash, labelIds->name, "1");
+    else
+        {
+        for(; labelEl; labelEl = labelEl->next)
+            hashAdd(labelHash, &labelEl->name[strlen(cartVar) + 1], labelEl->val);
+        }
+
+    struct asObject *as = asForDb(track->tdb, database);
+
+    for(thisLabel = labelIds; thisLabel; thisLabel = thisLabel->next)
+        {
+        char *str = hashFindVal(labelHash,thisLabel->name);
+        
+        if ((str != NULL) && sameString(str, "1"))
+            {
+            unsigned colNum = asColumnFindIx(as->columnList, thisLabel->name);
+
+            // put this column number in the list of columns to use to make label
+            slAddHead(&track->labelColumns, slIntNew(colNum));
+            }
+        }
+    slReverse(&track->labelColumns);
+    }
+}
+
 void loadSimpleBed(struct track *tg)
 /* Load the items in one track - just move beds in
  * window... */
 {
 struct bed *(*loader)(char **row);
 struct bed *bed, *list = NULL;
 char **row;
 int rowOffset;
 char *words[3];
 int wordCt;
 char query[128];
 char *setting = NULL;
 bool doScoreCtFilter = FALSE;
 int scoreFilterCt = 0;
 char *topTable = NULL;
@@ -91,34 +138,37 @@
     struct bedTabixFile *btf = bedTabixFileMayOpen(bigDataUrl, NULL, 0, 0);
     list = bedTabixReadBeds(btf, chromName, winStart, winEnd, loader);
     bedTabixFileClose(&btf);
     }
 else if (tg->isBigBed)
     { // avoid opening an unneeded db connection for bigBed; required not to use mysql for parallel fetch tracks
     char *scoreFilter = cartOrTdbString(cart, tg->tdb, "scoreFilter", NULL);
     struct lm *lm = lmInit(0);
     struct bigBedInterval *bb, *bbList = bigBedSelectRange(tg, chromName, winStart, winEnd, lm);
     char *bedRow[32];
     char startBuf[16], endBuf[16];
     int minScore = 0;
     if (scoreFilter)
 	minScore = atoi(scoreFilter);
 
+    tg->itemName = bigBedItemName;
+    calculateLabelFields(tg);
     for (bb = bbList; bb != NULL; bb = bb->next)
         {
         bigBedIntervalToRow(bb, chromName, startBuf, endBuf, bedRow, ArraySize(bedRow));
         bed = loader(bedRow);
+        bed->label = makeLabel(tg, bb);
         if (scoreFilter == NULL || bed->score >= minScore)
             slAddHead(&list, bed);
         }
     lmCleanup(&lm);
     }
 else
     {
     struct sqlConnection *conn = hAllocConnTrack(database, tg->tdb);
     struct sqlResult *sr = NULL;
     /* limit to items above a specified score */
     char *scoreFilterClause = getScoreFilterClause(cart, tg->tdb,NULL);
     if (doScoreCtFilter && (topTable != NULL) && hTableExists(database, topTable))
 	{
 	sqlSafef(query, sizeof(query),"select * from %s order by score desc limit %d",
 	      topTable, scoreFilterCt);
@@ -441,30 +491,31 @@
 {
 struct sqlResult *sr;
 char **row;
 int rowOffset;
 struct bed *bed;
 struct linkedFeatures *lfList = NULL, *lf;
 struct trackDb *tdb = tg->tdb;
 int scoreMin = atoi(trackDbSettingClosestToHomeOrDefault(tdb, "scoreMin", "0"));
 int scoreMax = atoi(trackDbSettingClosestToHomeOrDefault(tdb, "scoreMax", "1000"));
 boolean useItemRgb = FALSE;
 
 useItemRgb = bedItemRgb(tdb);
 
 if (tg->isBigBed)
     { // avoid opening an unneeded db connection for bigBed; required not to use mysql for parallel fetch tracks
+    calculateLabelFields(tg);
     bigBedAddLinkedFeaturesFrom(tg, chromName, winStart, winEnd,
           scoreMin, scoreMax, useItemRgb, 12, &lfList);
     }
 else
     {
     /* Use tg->tdb->track because subtracks inherit composite track's tdb
      * by default, and the variable is named after the composite track. */
     struct sqlConnection *conn = hAllocConn(database);
     char *scoreFilterClause = getScoreFilterClause(cart, tg->tdb,NULL);
     if (scoreFilterClause != NULL)
 	{
 	sr = hRangeQuery(conn, tg->table, chromName, winStart, winEnd,scoreFilterClause, &rowOffset);
 	freeMem(scoreFilterClause);
 	}
     else