65490776b922ad5a6ba63214bc015c3f61c5fad2 jcasper Wed Jun 12 14:56:14 2019 -0700 Adding custom track support for hic plus a couple cosmetic changes, refs #18842 diff --git src/hg/lib/customFactory.c src/hg/lib/customFactory.c index 6ca66a9..cec2e36 100644 --- src/hg/lib/customFactory.c +++ src/hg/lib/customFactory.c @@ -35,30 +35,31 @@ #include "bigWig.h" #include "bigBed.h" #include "hgBam.h" #include "vcf.h" #include "makeItemsItem.h" #include "bedDetail.h" #include "pgSnp.h" #include "regexHelper.h" #include "chromInfo.h" #include "trackHub.h" #include "bedTabix.h" #include "barChartBed.h" #include "barChartUi.h" #include "interact.h" #include "interactUi.h" +#include "hic.h" #include "cgiApoptosis.h" // placeholder when custom track uploaded file name is not known #define CT_NO_FILE_NAME "custom track" static boolean doExtraChecking = FALSE; /*** Utility routines used by many factories. ***/ char *customFactoryNextTilTrack(struct customPp *cpp) /* Return next line. Return NULL at end of input or at line starting with * "track." */ { char *line = customPpNext(cpp); if (line != NULL && startsWithWord("track", line)) @@ -132,30 +133,107 @@ { if (a == NULL && b == NULL) errAbort("sameType should not be called when both inputs are NULL."); else if (a == NULL || b == NULL) return FALSE; char *aCopy = cloneString(a); char *bCopy = cloneString(b); char *aWord = firstWordInLine(aCopy); char *bWord = firstWordInLine(bCopy); boolean same = sameString(aWord, bWord); freeMem(aCopy); freeMem(bCopy); return same; } +boolean isValidBigDataUrl(char *url, boolean doAbort) +/* return True if the URL is a valid bigDataUrl. + * It can be a local filename if this is allowed by udc.localDir + */ +{ +if ((startsWith("http://", url) + || startsWith("https://", url) + || startsWith("ftp://", url))) +return TRUE; + +// we allow bigDataUrl's to point to trash (or sessionDataDir, if configured) +char *sessionDataDir = cfgOption("sessionDataDir"); +char *sessionDataDirOld = cfgOption("sessionDataDirOld"); +if (startsWith(trashDir(), url) || + (isNotEmpty(sessionDataDir) && startsWith(sessionDataDir, url)) || + (isNotEmpty(sessionDataDirOld) && startsWith(sessionDataDirOld, url))) + return TRUE; + +char *prefix = cfgOption("udc.localDir"); +if (prefix == NULL) + { + if (doAbort) + errAbort("Only network protocols http, https, or ftp allowed in bigDataUrl: '%s'", url); + return FALSE; + } + +if (!startsWith(prefix, url)) + { + if (doAbort) + errAbort("bigDataUrl '%s' on local file system has to start with '%s' (see udc.localDir directive in cgi-bin/hg.conf)", url, prefix); + return FALSE; + } + +return TRUE; +} + +static void checkAllowedBigDataUrlProtocols(char *url) +/* Abort if url is not using one of the allowed bigDataUrl network protocols. + * In particular, do not allow a local file reference, unless explicitely + * allowed by hg.conf's udc.localDir directive. */ +{ +isValidBigDataUrl(url, TRUE); +} + +static char *bigDataDocPath(char *type) +/* If type is a bigData type, provide a relative path to its custom track/format doc page. */ +{ +char *docUrl = NULL; +if (sameString(type, "bigWig")) + docUrl = "../goldenPath/help/bigWig.html"; +else if (sameString(type, "bigBed")) + docUrl = "../goldenPath/help/bigBed.html"; +else if (sameString(type, "bam")) + docUrl = "../goldenPath/help/bam.html"; +else if (sameString(type, "vcfTabix")) + docUrl = "../goldenPath/help/vcf.html"; +return docUrl; +} + +static void requireBigDataUrl(char *bigDataUrl, char *type, char *trackName) +/* If bigDataUrl is empty, errAbort with helpful message about bigDataUrl requirement */ +{ +if (isEmpty(bigDataUrl)) + { + struct dyString *doc = dyStringNew(0); + char *docUrl = bigDataDocPath(type); + if (docUrl != NULL) + dyStringPrintf(doc, " For more information about the bigDataUrl setting, see " + "%s custom track documentation.", + docUrl, type); + errAbort("Missing bigDataUrl setting from track of type=%s (%s). " + "Please check for case and spelling and that there is no new-line " + "between the 'track' and the 'bigDataUrl' if the bigDataUrl appears to be there." + "%s", + type, trackName, doc->string); + } +} /*** BED Factory ***/ static boolean rowIsBed(char **row, int wordCount, char *db, struct dyString *reason) /* Return TRUE if row is consistent with BED format. If it's not BED and reason is not NULL, * append reason for failure. */ { if (wordCount < 3) { if (reason) dyStringAppend(reason, "Too few fields (need at least 3)"); return FALSE; } if (wordCount > bedKnownFields) { if (reason) @@ -1551,30 +1629,74 @@ slAddHead(&itemList, item); } slReverse(&itemList); return interactFinish(track, itemList); } struct customFactory interactFactory = /* Factory for interact tracks */ { NULL, "interact", interactRecognizer, interactLoader, }; + +/*********************/ +/**** hic Factory ****/ + +static boolean hicRecognizer(struct customFactory *fac, + struct customPp *cpp, char *type, + struct customTrack *track) +/* Return TRUE if looks like we're handling a hic track */ +{ +return (sameType(type, "hic")); +} + +static struct customTrack *hicLoader(struct customFactory *fac, + struct hash *chromHash, + struct customPp *cpp, struct customTrack *track, boolean dbRequested) +/* Load up hic data until get next track line. */ +{ +struct hash *settings = track->tdb->settingsHash; +char *bigDataUrl = hashFindVal(settings, "bigDataUrl"); +requireBigDataUrl(bigDataUrl, fac->name, track->tdb->shortLabel); +checkAllowedBigDataUrlProtocols(bigDataUrl); + +if (doExtraChecking) + { + struct hicMeta *meta; + char *hicErrMsg = hicLoadHeader(bigDataUrl, &meta); + if (hicErrMsg != NULL) + { + track->networkErrMsg = cloneString(hicErrMsg); + } + } +return track; +} + +struct customFactory hicFactory = +/* Factory for Hi-C tracks */ +{ + NULL, + "hic", + hicRecognizer, + hicLoader, + }; + + /*** GFF/GTF Factory - converts to BED ***/ static boolean rowIsGff(char *db, char **row, int wordCount, char *type, struct lineFile *lf) /* Return TRUE if format of this row is consistent with being a .gff */ { boolean isGff = FALSE; if (wordCount >= 8 && wordCount <= 9) { /* Check that strand is + - or . */ char *strand = row[6]; char c = strand[0]; if ((c == '.' || c == '+' || c == '-') && strand[1] == 0) { // check chrom name char *officialChrom = hgOfficialChromName(db, row[0]); @@ -2548,108 +2670,30 @@ struct hash *settings = track->tdb->settingsHash; if (hashLookup(settings, "viewLimits") == NULL) { struct bbiSummaryElement sum = bbiTotalSummary(track->bbiFile); if (sum.minVal == sum.maxVal) { sum.minVal += 1; sum.maxVal -= 1; } char text[1024]; safef(text, sizeof(text), "%f:%f", sum.minVal, sum.maxVal); hashAdd(settings, "viewLimits", cloneString(text)); } } -boolean isValidBigDataUrl(char *url, boolean doAbort) -/* return True if the URL is a valid bigDataUrl. - * It can be a local filename if this is allowed by udc.localDir - */ -{ -if ((startsWith("http://", url) - || startsWith("https://", url) - || startsWith("ftp://", url))) -return TRUE; - -// we allow bigDataUrl's to point to trash (or sessionDataDir, if configured) -char *sessionDataDir = cfgOption("sessionDataDir"); -char *sessionDataDirOld = cfgOption("sessionDataDirOld"); -if (startsWith(trashDir(), url) || - (isNotEmpty(sessionDataDir) && startsWith(sessionDataDir, url)) || - (isNotEmpty(sessionDataDirOld) && startsWith(sessionDataDirOld, url))) - return TRUE; - -char *prefix = cfgOption("udc.localDir"); -if (prefix == NULL) - { - if (doAbort) - errAbort("Only network protocols http, https, or ftp allowed in bigDataUrl: '%s'", url); - return FALSE; - } - -if (!startsWith(prefix, url)) - { - if (doAbort) - errAbort("bigDataUrl '%s' on local file system has to start with '%s' (see udc.localDir directive in cgi-bin/hg.conf)", url, prefix); - return FALSE; - } - -return TRUE; -} - -static void checkAllowedBigDataUrlProtocols(char *url) -/* Abort if url is not using one of the allowed bigDataUrl network protocols. - * In particular, do not allow a local file reference, unless explicitely - * allowed by hg.conf's udc.localDir directive. */ -{ -isValidBigDataUrl(url, TRUE); -} - -static char *bigDataDocPath(char *type) -/* If type is a bigData type, provide a relative path to its custom track/format doc page. */ -{ -char *docUrl = NULL; -if (sameString(type, "bigWig")) - docUrl = "../goldenPath/help/bigWig.html"; -else if (sameString(type, "bigBed")) - docUrl = "../goldenPath/help/bigBed.html"; -else if (sameString(type, "bam")) - docUrl = "../goldenPath/help/bam.html"; -else if (sameString(type, "vcfTabix")) - docUrl = "../goldenPath/help/vcf.html"; -return docUrl; -} - -static void requireBigDataUrl(char *bigDataUrl, char *type, char *trackName) -/* If bigDataUrl is empty, errAbort with helpful message about bigDataUrl requirement */ -{ -if (isEmpty(bigDataUrl)) - { - struct dyString *doc = dyStringNew(0); - char *docUrl = bigDataDocPath(type); - if (docUrl != NULL) - dyStringPrintf(doc, " For more information about the bigDataUrl setting, see " - "%s custom track documentation.", - docUrl, type); - errAbort("Missing bigDataUrl setting from track of type=%s (%s). " - "Please check for case and spelling and that there is no new-line " - "between the 'track' and the 'bigDataUrl' if the bigDataUrl appears to be there." - "%s", - type, trackName, doc->string); - } -} - static struct customTrack *bigWigLoader(struct customFactory *fac, struct hash *chromHash, struct customPp *cpp, struct customTrack *track, boolean dbRequested) /* Load up wiggle data until get next track line. */ { /* Not much to this. A bigWig has nothing here but a track line. */ struct hash *settings = track->tdb->settingsHash; char *bigDataUrl = hashFindVal(settings, "bigDataUrl"); requireBigDataUrl(bigDataUrl, fac->name, track->tdb->shortLabel); checkAllowedBigDataUrlProtocols(bigDataUrl); /* protect against temporary network error */ struct errCatch *errCatch = errCatchNew(); if (errCatchStart(errCatch)) { @@ -3431,30 +3475,31 @@ slAddTail(&factoryList, &bigBedFactory); slAddTail(&factoryList, &bedGraphFactory); slAddTail(&factoryList, µarrayFactory); slAddTail(&factoryList, &coloredExonFactory); slAddTail(&factoryList, &encodePeakFactory); slAddTail(&factoryList, &bedDetailFactory); slAddTail(&factoryList, &adjacencyFactory); slAddTail(&factoryList, &bamFactory); slAddTail(&factoryList, &vcfTabixFactory); slAddTail(&factoryList, &makeItemsFactory); slAddTail(&factoryList, &bigDataOopsFactory); slAddTail(&factoryList, &barChartFactory); slAddTail(&factoryList, &bigBarChartFactory); slAddTail(&factoryList, &interactFactory); slAddTail(&factoryList, &bigInteractFactory); + slAddTail(&factoryList, &hicFactory); } } struct customFactory *customFactoryFind(char *genomeDb, struct customPp *cpp, char *type, struct customTrack *track) /* Figure out factory that can handle this track. The track is * loaded from the track line if any, and type is the type element * if any from that track. */ { struct customFactory *fac; customFactoryInit(); for (fac = factoryList; fac != NULL; fac = fac->next) if (fac->recognizer(fac, cpp, type, track)) break; return fac;