src/hg/lib/wiggleCart.c 1.26
1.26 2009/11/17 06:37:58 angie
Refactored wigFetchMinMaxYWithCart. It is still quite complex because I kept the old behavior that tolerates missing data range specifications (although it does warn now). When hgTrackDb is enhanced to enforce data range specs, this can become a lot simpler -- but still not simple because we still allow the viewLimits setting (not defaultViewLimits) to push out the data range boundaries.
Index: src/hg/lib/wiggleCart.c
===================================================================
RCS file: /projects/compbio/cvsroot/kent/src/hg/lib/wiggleCart.c,v
retrieving revision 1.25
retrieving revision 1.26
diff -b -B -U 4 -r1.25 -r1.26
--- src/hg/lib/wiggleCart.c 10 Nov 2009 07:53:58 -0000 1.25
+++ src/hg/lib/wiggleCart.c 17 Nov 2009 06:37:58 -0000 1.26
@@ -73,254 +73,179 @@
#endif
#endif
-static void wigMaxLimitsCompositeOverride(struct trackDb *tdb,char *name,double *min, double *max,double *tDbMin, double *tDbMax)
+static void parseColonRange(char *setting, double *retMin, double *retMax)
+/* Parse setting's two colon-separated numbers into ret{Min,Max}, unless setting
+ * is NULL or empty or retMin/retMax is NULL. errAbort if invalid format. */
+{
+if (isNotEmpty(setting))
+ {
+ char tmp[64]; // Intentionally small -- should be only 2 floating point #s + ':'
+ safecpy(tmp, sizeof(tmp), setting);
+ char *words[3];
+ if (chopByChar(tmp, ':', words, ArraySize(words)) == 2)
+ {
+ double low = sqlDouble(words[0]);
+ double high = sqlDouble(words[1]);
+ correctOrder(low, high);
+ if (retMin)
+ *retMin = low;
+ if (retMax)
+ *retMax = high;
+ }
+ else
+ errAbort("Can't parse colon range '%s'", setting);
+ }
+}
+
+static void viewLimitsCompositeOverride(struct trackDb *tdb, char *name,
+ double *retMin, double *retMax, double *retAbsMin, double *retAbsMax)
/* If aquiring min/max for composite level wig cfg, look for trackDb.ra "settingsByView" */
{
if(isNameAtCompositeLevel(tdb,name))
{
char *setting = trackDbSettingByView(tdb,VIEWLIMITSMAX);
- if(setting != NULL)
- {
- char *word = strSwapChar(setting,':',' ');
- if(tDbMin != NULL && word[0] != 0)
- *tDbMin = sqlDouble(nextWord(&word));
- if(tDbMax != NULL && word[0] != 0)
- *tDbMax = sqlDouble(word);
- if(tDbMin != NULL && tDbMax != NULL)
- correctOrder(*tDbMin,*tDbMax);
+ parseColonRange(setting, retAbsMin, retAbsMax);
freez(&setting);
- }
setting = trackDbSettingByView(tdb,VIEWLIMITS);
- if(setting != NULL)
- {
- char *word = strSwapChar(setting,':',' ');
- assert(min != NULL && max != NULL);
- *min = sqlDouble(nextWord(&word));
- if(word[0] != 0)
- *max = sqlDouble(word);
- correctOrder(*min,*max);
+ parseColonRange(setting, retMin, retMax);
freez(&setting);
}
- }
}
/*****************************************************************************
* Min, Max Y viewing limits
- * Absolute limits are defined on the trackDb type line
+ * Absolute limits are defined on the trackDb type line for wiggle,
+ * or MIN_LIMIT / MAX_LIMIT trackDb settings for bedGraph
* User requested limits are defined in the cart
* Default opening display limits are optionally defined with the
- * defaultViewLimits declaration from trackDb
- * or viewLimits from custom tracks
- * (both identifiers work from either custom or trackDb)
- * [JK Comment - this is a horrible, horrible routine! Should be maybe 10 lines, not 200, no?]
+ * defaultViewLimits or viewLimits declaration from trackDb
*****************************************************************************/
void wigFetchMinMaxYWithCart(struct cart *theCart, struct trackDb *tdb, char *name,
- double *min, double *max, double *absMax, double *absMin, int wordCount, char **words)
+ double *retMin, double *retMax, double *retAbsMin, double *retAbsMax,
+ int wordCount, char **words)
{
-boolean compositeLevel = isNameAtCompositeLevel(tdb,name);
-char *minY_str = NULL; /* string from cart */
-char *maxY_str = NULL; /* string from cart */
-double minY, maxY; /* Current min/max for view. */
-double absMinY=0, absMaxY=0; /* Absolute min/max allowed. */
-char * tdbDefault = cloneString(trackDbSettingClosestToHomeOrDefault(tdb, DEFAULTVIEWLIMITS, "NONE"));
-double defaultViewMinY = 0.0; /* optional default viewing window */
-double defaultViewMaxY = 0.0; /* can be different than absolute min,max */
-char * viewLimits = (char *)NULL;
-char * settingsMin = (char *)NULL;
-char * settingsMax = (char *)NULL;
boolean isBedGraph = (wordCount == 0 || sameString(words[0],"bedGraph"));
-
-/* Allow the word "viewLimits" to be recognized too */
-if (sameWord("NONE",tdbDefault))
- {
- freeMem(tdbDefault);
- tdbDefault = cloneString(trackDbSettingClosestToHomeOrDefault(tdb, VIEWLIMITS, "NONE"));
- }
-
-/* And check for either viewLimits in custom track settings */
-if ((tdb->settings != (char *)NULL) &&
- (tdb->settingsHash != (struct hash *)NULL))
- {
- struct hashEl *hel;
- if ((hel = hashLookup(tdb->settingsHash, VIEWLIMITS)) != NULL)
- viewLimits = cloneString((char *)hel->val);
- else if ((hel = hashLookup(tdb->settingsHash, DEFAULTVIEWLIMITS)) != NULL)
- viewLimits = cloneString((char *)hel->val);
- if(isBedGraph)
- {
- if ((hel = hashLookup(tdb->settingsHash, MIN_LIMIT)) != NULL)
- settingsMin = cloneString((char *)hel->val);
- if ((hel = hashLookup(tdb->settingsHash, MAX_LIMIT)) != NULL)
- settingsMax = cloneString((char *)hel->val);
- }
- }
-
-/* If no viewLimits from trackDb, if in settings, use them. */
-if (sameWord("NONE",tdbDefault) && (viewLimits != (char *)NULL))
- {
- freeMem(tdbDefault);
- tdbDefault = cloneString(viewLimits);
- }
-
-/* Assume last resort defaults, these should never be used
- * The only case they would be used is if trackDb settings are not
- * there or can not be parsed properly
- * A trackDb wiggle track entry should read with three words:
- * type wig min max
- * where min and max are floating point numbers (integers OK)
- */
-if(isBedGraph)
- {
- *min = minY = DEFAULT_MIN_BED_GRAPH;
- *max = maxY = DEFAULT_MAX_BED_GRAPH;
+// Determine absolute min and max. Someday hgTrackDb will enforce inclusion of data
+// range settings, but until then, there is some circular logic where either one
+// can provide a default for the other if the other is missing.
+int absMax = 0.0, absMin = 0.0;
+boolean missingAbsMin = FALSE, missingAbsMax = FALSE;
+if (isBedGraph)
+ {
+ char *tdbMin = trackDbSettingClosestToHomeOrDefault(tdb, MIN_LIMIT, NULL);
+ char *tdbMax = trackDbSettingClosestToHomeOrDefault(tdb, MAX_LIMIT, NULL);
+ if (tdbMin == NULL)
+ missingAbsMin = TRUE;
+ else
+ absMin = sqlDouble(tdbMin);
+ if (tdbMax == NULL)
+ missingAbsMax = TRUE;
+ else
+ absMax = sqlDouble(tdbMax);
}
else
{
- *min = minY = DEFAULT_MIN_Yv;
- *max = maxY = DEFAULT_MAX_Yv;
- }
-
-/* Check to see if custom track viewLimits will override the
- * track type wig limits. When viewLimits are greater than the
- * type wig limits, use the viewLimits. This is necessary because
- * the custom track type wig limits are exactly at the limits of
- * the data input and thus it is better if viewLimits are set
- * outside the limits of the data so it will look better in the
- * graph.
- */
-if (viewLimits)
- {
- char *words[2];
- char *sep = ":";
- int wordCount = chopString(viewLimits,sep,words,ArraySize(words));
- double viewMin;
- double viewMax;
- if (wordCount == 2)
- {
- viewMin = atof(words[0]);
- viewMax = atof(words[1]);
- /* make sure they are in order */
- correctOrder(viewMin,viewMax);
-
- /* and they had better be different */
- if (! ((viewMax - viewMin) > 0.0))
- {
- viewMax = viewMin = 0.0; /* failed the test */
- }
+ // Wiggle: get min and max from type setting, which has been chopped into words and wordCount:
+ // type wig <min> <max>
+ if (wordCount >= 3)
+ absMax = atof(words[2]);
else
{
- minY = viewMin;
- maxY = viewMax;
- }
- }
- }
-
-*min = minY;
-*max = maxY;
-
-/* See if a default viewing window is specified in the trackDb.ra file
- * Yes, it is true, this parsing is paranoid and verifies that the
- * input values are valid in order to be used. If they are no
- * good, they are as good as not there and the result is a pair of
- * zeros and they are not used.
- */
-if (differentWord("NONE",tdbDefault))
- {
- char *words[2];
- char *sep = ":";
- int wordCount = chopString(tdbDefault,sep,words,ArraySize(words));
+ missingAbsMax = TRUE;
if (wordCount == 2)
- {
- defaultViewMinY = atof(words[0]);
- defaultViewMaxY = atof(words[1]);
- /* make sure they are in order */
- correctOrder(defaultViewMinY,defaultViewMaxY);
-
- /* and they had better be different */
- if (! ((defaultViewMaxY - defaultViewMinY) > 0.0))
- {
- defaultViewMaxY = defaultViewMinY = 0.0; /* failed the test */
- }
+ absMin = atof(words[1]);
else
- {
- *min = defaultViewMinY;
- *max = defaultViewMaxY;
- }
+ missingAbsMin = TRUE;
}
}
+correctOrder(absMin, absMax);
-/* Calculate absolute min/max */
-if(isBedGraph)
+// Determine current minY,maxY.
+// Precedence: 1. cart; 2. defaultViewLimits; 3. viewLimits;
+// 4. absolute [which may need to default to #2 or #3!]
+boolean compositeLevel = isNameAtCompositeLevel(tdb, name);
+char *cartMinStr = cartOptionalStringClosestToHome(theCart, tdb, compositeLevel, MIN_Y);
+char *cartMaxStr = cartOptionalStringClosestToHome(theCart, tdb, compositeLevel, MAX_Y);
+double cartMin = 0.0, cartMax = 0.0;
+if (cartMinStr && cartMaxStr)
+ {
+ *retMin = atof(cartMinStr);
+ *retMax = atof(cartMaxStr);
+ correctOrder(*retMin, *retMax);
+ // If it weren't for the expansion of absM* to viewLimits bounds,
+ // and the allowance for missing data range values,
+ // we could set retAbs* and be done here.
+ cartMin = *retMin;
+ cartMax = *retMax;
+ }
+
+// Get trackDb defaults, and resolve missing wiggle data range if necessary.
+char *defaultViewLimits = trackDbSettingClosestToHomeOrDefault(tdb, DEFAULTVIEWLIMITS, NULL);
+char *viewLimits = trackDbSettingClosestToHomeOrDefault(tdb, VIEWLIMITS, NULL);
+if (defaultViewLimits == NULL)
+ defaultViewLimits = viewLimits;
+if (defaultViewLimits != NULL)
+ {
+ double viewLimitMin = 0.0, viewLimitMax = 0.0;
+ parseColonRange(defaultViewLimits, &viewLimitMin, &viewLimitMax);
+ *retMin = viewLimitMin;
+ *retMax = viewLimitMax;
+ if (missingAbsMax)
+ absMax = viewLimitMax;
+ if (missingAbsMin)
+ absMin = viewLimitMin;
+ // If viewLimits (not defaultViewLimits) extends beyond data value range,
+ // tweak absMin and absMax to show the union:
+ if (viewLimits != NULL)
+ {
+ parseColonRange(viewLimits, &viewLimitMin, &viewLimitMax);
+ absMin = min(absMin, viewLimitMin);
+ absMax = max(absMax, viewLimitMax);
+ }
+ }
+else if (missingAbsMin || missingAbsMax)
+ {
+ // I would like to make this an errAbort, but too many tracks are afflicted
+ // to do that until hgTrackDb helps to enforce:
+ warn("trackDb %s, and no default view limits are specified for track %s",
+ (isBedGraph ? MIN_LIMIT " and/or " MAX_LIMIT " is not specified" :
+ "'type wig' line is missing min and/or max data value"),
+ tdb->tableName);
+ // When that becomes an errAbort, remove these defines from wiggle.h:
+ if (isBedGraph)
{
- char * tdbMin = cloneString(trackDbSettingClosestToHomeOrDefault(tdb, MIN_LIMIT, "NONE"));
- char * tdbMax = cloneString(trackDbSettingClosestToHomeOrDefault(tdb, MAX_LIMIT, "NONE"));
- if (sameWord("NONE",tdbMin))
- {
- if (settingsMin != (char *)NULL)
- absMinY = sqlDouble(settingsMin);
+ absMin = DEFAULT_MIN_BED_GRAPH;
+ absMax = DEFAULT_MAX_BED_GRAPH;
}
else
- absMinY = sqlDouble(tdbMin);
-
- if (sameWord("NONE",tdbMax))
{
- if (settingsMax != (char *)NULL)
- absMaxY = sqlDouble(settingsMax);
+ absMin = DEFAULT_MIN_Yv;
+ absMax = DEFAULT_MAX_Yv;
}
- else
- absMaxY = sqlDouble(tdbMax);
- freeMem(tdbMax);
- freeMem(tdbMin);
- freeMem(settingsMax);
- freeMem(settingsMin);
+ *retMin = absMin;
+ *retMax = absMax;
}
else
{
- /* Let's see what trackDb has to say about these things,
- * these words come from the trackDb.ra type wig line:
- * type wig <min> <max>
- */
- double diff = abs(maxY - minY);
- absMinY = minY - diff*0.10;
- absMaxY = maxY + diff*0.10;
- switch (wordCount)
- {
- case 3:
- absMaxY = atof(words[2]);
- // Fall through
- case 2:
- absMinY = atof(words[1]);
- break;
- default:
- break;
- }
- }
-correctOrder(absMinY,absMaxY);
-/* return absolut min/max if OK to do that */
-if (absMin)
- *absMin = absMinY;
-if (absMax)
- *absMax = absMaxY;
-
-
-// One more step: If we are loading settings for a composite view, then look for overrides
-wigMaxLimitsCompositeOverride(tdb,name,min,max,absMin,absMax);
-
-/* And finally, let's see if values are available in the cart */
-minY_str = cartOptionalStringClosestToHome(theCart, tdb, compositeLevel, MIN_Y);
-maxY_str = cartOptionalStringClosestToHome(theCart, tdb, compositeLevel, MAX_Y);
-
-if (minY_str && maxY_str) /* if specified in the cart */
- {
- *min = max( minY, atof(minY_str));
- *max = min( maxY, atof(maxY_str));
- correctOrder(*min,*max);
+ *retMin = absMin;
+ *retMax = absMax;
}
-freeMem(tdbDefault);
-freeMem(viewLimits);
+if (retAbsMin)
+ *retAbsMin = absMin;
+if (retAbsMax)
+ *retAbsMax = absMax;
+// After the dust settles from tdb's trackDb settings, now see if composite view
+// settings from tdb's parents override that stuff anyway:
+viewLimitsCompositeOverride(tdb, name, retMin, retMax, retAbsMin, retAbsMax);
+// And as the final word after composite override, reset retMin and retMax if from cart:
+if (cartMinStr && cartMaxStr)
+ {
+ *retMin = max(absMin, cartMin);
+ *retMax = min(absMax, cartMax);
+ }
} /* void wigFetchMinMaxYWithCart() */
/* Min, Max, Default Pixel height of track
* Limits may be defined in trackDb with the maxHeightPixels string,