src/hg/instinct/hgHeatmap2/hgCircleMaps.c 1.7

1.7 2009/02/27 21:26:46 jsanborn
updated hgcirclemaps
Index: src/hg/instinct/hgHeatmap2/hgCircleMaps.c
===================================================================
RCS file: /projects/compbio/cvsroot/kent/src/hg/instinct/hgHeatmap2/hgCircleMaps.c,v
retrieving revision 1.6
retrieving revision 1.7
diff -b -B -U 4 -r1.6 -r1.7
--- src/hg/instinct/hgHeatmap2/hgCircleMaps.c	28 Jan 2009 22:44:18 -0000	1.6
+++ src/hg/instinct/hgHeatmap2/hgCircleMaps.c	27 Feb 2009 21:26:46 -0000	1.7
@@ -42,8 +42,9 @@
 
     double median;           /* median value for ring */
     boolean drawSummary;     /* draw summary */
 
+    boolean visible;         /* invisible rings exist, can be sorted, not displayed */
     boolean sort;            /* Sort this ring or not */
     int sortDir;             /* +1 = ASC, -1 = DESC */
     double maxVal;           /* maximum value, used for sorting */
 
@@ -64,8 +65,10 @@
     double maxR;             /* max radius for all rings */
     int width;               /* pix width */
 
     boolean drawSummary;     /* draw summary (pie chart) */
+    boolean subgrouped;      /* if successful subgrouping applied */
+
     struct hash *data;       /* data for center (pie chart) */
 };
 
 char *heatMapDbProfile = "localDb";
@@ -83,8 +86,11 @@
 
 void vgRadiusBox(struct vGfx *vg, int x0, int y0, double rStart, double rStop, 
 		 double tStart, double tStop, Color col)
 {
+if ((rStart == rStop) || (tStart == tStop)) // zero height/thickness
+    return;
+
 struct gfxPoly *gp = gfxPolyNew();
 
 int x, y;
 double r, t, tStep;
@@ -139,13 +145,44 @@
 cm->width = 0;
 cm->data = NULL;
 
 cm->drawSummary = FALSE;
+cm->subgrouped = FALSE;
 
 return cm;
 }
 
-struct ring *addRingToCircleMap(struct circleMap *cm, char *name, struct genoHeatmap *gh)
+struct ring *cloneRing(struct ring *rg)
+{
+if (!rg)
+    return NULL;
+
+struct ring *rg2;
+AllocVar(rg2);
+rg2->name = cloneString(rg->name);
+rg2->dataset = cloneString(rg->dataset);
+rg2->type = cloneString(rg->type);
+rg2->maxDev = rg->maxDev;
+rg2->gain = rg->gain;
+rg2->numElements = rg->numElements;
+rg2->minR = rg->minR;
+rg2->maxR = rg->maxR;
+rg2->data = NULL;
+
+rg2->drawSummary = rg->drawSummary;
+rg2->median = 0.0;
+
+rg2->visible = rg->visible;
+
+rg2->sort = rg->sort;   
+rg2->sortDir = rg->sortDir;
+rg2->maxVal = rg->maxVal;
+
+return rg2;
+}
+
+struct ring *addRingToCircleMap(struct circleMap *cm, char *name, struct genoHeatmap *gh, 
+				boolean isVisible)
 {  /* Initialize a ring, add it circleLay, and return reference to it */ 
 struct ring *rg;
 AllocVar(rg);
 rg->name = cloneString(name);
@@ -160,8 +197,10 @@
 
 rg->drawSummary = FALSE;
 rg->median = 0.0;
 
+rg->visible = isVisible;
+
 rg->sort = FALSE;   
 rg->sortDir = 0;
 rg->maxVal = 0.0;
 
@@ -471,9 +510,16 @@
 
 double i = 0.0;
 struct slName *sl;
 struct slDouble *sd, *sdList;
-Color lightGray = vgFindColorIx(vg, 225, 225, 225); 
+
+Color missingColor;
+if (cm->subgrouped)
+    missingColor = MG_WHITE;  // when subgrouped, gray is distracting
+else
+    missingColor = vgFindColorIx(vg, 225, 225, 225);  // light gray
+
+
 for (sl = cm->samples; sl; sl = sl->next)
     {
     tStart = 2.0 * PI * i / numSamples;
     tStop  = 2.0 * PI * (i + 1.0) / numSamples;
@@ -483,9 +529,9 @@
 
     struct hashEl *el = hashLookup(rg->data, sl->name);
     if (!el)
 	{
-	vgRadiusBox(vg, x0, y0, rg->minR, rg->maxR, tStart, tStop, lightGray);
+	vgRadiusBox(vg, x0, y0, rg->minR, rg->maxR, tStart, tStop, missingColor);
 	continue;
 	}
     sdList = el->val;  // slDouble list of expScores
 
@@ -565,22 +611,33 @@
 cm->width = width;
 cm->maxR = (double) cm->width / 2.0;
 cm->minR = 0.75 * cm->maxR;
 
-int numRings = slCount(cm->rings);
+struct ring *rg;
+int numRings = 0;
+for (rg = cm->rings; rg; rg = rg->next)
+    if (rg->visible)
+	numRings++;
+
 double step = (cm->maxR - cm->minR) / (double) numRings;
 
+double buffer = 0.0;
+if (cm->subgrouped)
+    buffer = step / 5.0;  // put buffer between subgrouped rings
+buffer = 0.0;
+
 double rStart = cm->minR;
-double rStop = cm->minR + step;
+double rStop = cm->minR + (step - buffer);
 
-struct ring *rg;
 for (rg = cm->rings; rg; rg = rg->next)
     {
+    if (!rg->visible)  // keep minR/maxR at zero for no display
+	continue;
     rg->minR = rStart;
     rg->maxR = rStop;
 
     rStart += step;
-    rStop += step;
+    rStop = rStart + (step - buffer);
     }
 }
 
 void setupSummary(struct circleMap *cm)
@@ -669,8 +726,10 @@
 }
 
 void sortCircleMap(struct circleMap *cm)
 { /* Sorting goes from inner ring out, averaging all probes within a ring for value */
+if (!cm->samples)
+    return;
 
 /* Setup sorting by gene value */
 setupGeneSort(cm);
 
@@ -688,9 +747,9 @@
     sn->name = cloneString(sl->name);
     sn->list = NULL;
     for (rg = cm->rings; rg; rg = rg->next)
 	{
-	if (!rg->sort)
+	if (!rg->sort || !rg->data)
 	    continue;
 	struct hashEl *el = hashLookup(rg->data, sn->name);
 	if (!el)
 	    val = rg->maxVal + 0.1;  // if no val, set all to above inner ring's max
@@ -747,22 +805,32 @@
 			gh->name, NULL, gs);    
     if (!geneHash)   // gene hash could not be allocated, bad.
 	continue;
     
-    struct ring *rg = addRingToCircleMap(cm, gene, gh);
+    struct ring *rg = addRingToCircleMap(cm, gene, gh, TRUE);
     addGeneDataToRing(rg, geneHash, gene, gh->sampleList); 
 
     hashFree(&geneHash);
     }
 slReverse(&cm->rings);
 }
 
-void addFeatureToCircleMap(struct circleMap *cm, char *datasets)
+void addFeaturesToCircleMap(struct circleMap *cm, char *datasets)
 {
 char *featureList = cartOptionalString(cart, hgh2FeatureList); 
-if (!featureList)
+char *featureSort = cartOptionalString(cart, hgh2FeatureSort); 
+
+if (!featureList && !featureSort)
     return;
 
+boolean isVisible = TRUE;
+if (!featureList)
+    { /* in order for sorting to work, need features in rings,
+       * but should be invisible unless featureList was set */
+    isVisible = FALSE;
+    featureList = featureSort;
+    }
+
 struct genoHeatmap *gh = NULL;
 struct slName *sl, *slList = slNameListFromComma(datasets);
 for (sl = slList; sl; sl = sl->next)
     {
@@ -794,9 +862,9 @@
 	    break;
     if (!col)
 	continue;
     
-    struct ring *rg = addRingToCircleMap(cm, col->name, gh); 
+    struct ring *rg = addRingToCircleMap(cm, col->name, gh, isVisible); 
     addFeatureDataToRing(conn, rg, col, gh);
     }
 hFreeConn(&conn);
 }
@@ -835,8 +903,11 @@
 }
 
 boolean needSplit(struct ring *rg, struct slName **ptSubsets, int subsetNum)
 {
+if (!rg->data)   // no data, no split!
+    return FALSE;
+
 int subset;
 struct slName *sl;
 
 int needSplit = 0;
@@ -847,26 +918,102 @@
 	struct hashEl *el = hashLookup(rg->data, sl->name);
 	if (!el)
 	    continue;
 	needSplit++;
+	break;
 	}
     }
 
 return (needSplit > 1);
 }
 
+void removeSamplesFromRing(struct circleMap *cm, struct ring *rg)
+{
+if (!rg->data)  // nothing to remove
+    return;
+
+struct slName *sl, *slList = NULL;
+for (sl = cm->samples; sl; sl = sl->next)
+    {
+    struct hashEl *el = hashLookup(rg->data, sl->name);
+    if (!el)  // not in ring, so keep sample name in circle's list 
+	slNameAddHead(&slList, sl->name);
+    }
+slReverse(&slList);            // maintain original order
+
+slNameFreeList(&cm->samples);  // remove old sample list
+cm->samples = slList;          // set to new list.
+}
+
+
+boolean removeRing(struct circleMap *cm, struct ring *toRemove)
+{
+/* Delete any circleMap samples on the to-be-removed ring */
+removeSamplesFromRing(cm, toRemove);
+
+if (cm->rings == toRemove) /* ring to remove is first, set to next */
+    {
+    cm->rings = toRemove->next;
+    return TRUE;
+    }
+
+struct ring *rg, *nextRg;
+for (rg = cm->rings; rg; rg = rg->next)
+    {
+    nextRg = rg->next;
+    if (nextRg == toRemove)
+	{
+	rg->next = nextRg->next;
+	nextRg->next = NULL;
+	return TRUE;
+	}
+    }
+
+return FALSE;
+}
+
 void splitRing(struct circleMap *cm, struct ring *rg, 
 	       struct slName **ptSubsets, int subsetNum)
 {
-return;
+int subset;
+struct ring *rg2, *ptRings[subsetNum];
+for (subset = 0; subset < subsetNum; subset++)
+    ptRings[subset] = cloneRing(rg);
+
+struct slName *sl;
+for (subset = 0; subset < subsetNum; subset++)
+    {
+    rg2 = ptRings[subset];
+    rg2->data = hashNew(0);
+
+    for (sl = ptSubsets[subset]; sl; sl = sl->next)
+	{
+	struct hashEl *el = hashLookup(rg->data, sl->name);
+	if (!el)
+	    continue;
+	struct slDouble *sdList = el->val;
+
+	hashAdd(rg2->data, sl->name, sdList);
+	hashRemove(rg->data, sl->name);
+	}
+    }
+
+/* Remove empty ring that was split */
+boolean removed = removeRing(cm, rg);
+if (!removed)
+    errAbort("Problem with removing ring");
+
+/* Add new rings */
+for (subset = 0; subset < subsetNum; subset++)
+    slAddHead(&cm->rings, ptRings[subset]);
 }
 
 void splitCircleMap(struct circleMap *cm, struct slName **ptSubsets, int subsetNum)
 {
-struct ring *rg;
-
-for (rg = cm->rings; rg; rg = rg->next)
+struct ring *rg, *nextRg;
+for (rg = cm->rings; rg; rg = nextRg)
     {
+    nextRg = rg->next;
     if (!needSplit(rg, ptSubsets, subsetNum))
 	continue;
     splitRing(cm, rg, ptSubsets, subsetNum);
     }    
@@ -897,8 +1044,9 @@
     if (!ifSubsets)
 	continue;
 
     splitCircleMap(cm, ptSubsets, subsetNum);
+    cm->subgrouped = TRUE;
     }
 }
 
 
@@ -910,21 +1058,21 @@
 /* Add gene data (if gene is set) */
 addGeneToCircleMap(cm, datasets, gene);
 
 /* Add feature data (if featureList is set) */
-addFeatureToCircleMap(cm, datasets);
+addFeaturesToCircleMap(cm, datasets);
 
 /* Set up any subgrouping info */
 setupSubgroups(cm, datasets);
 
-/* Layout circle map, setting radial widths */
-layoutCircleMap(cm, width);
-
 /* Setup any summary (displaying only median value) */
 setupSummary(cm);
 
 /* Sort circle map data */
 sortCircleMap(cm);
+
+/* Layout circle map, setting radial widths */
+layoutCircleMap(cm, width);
 }
 
 void modeGetCircleMap()
 {