8c86cfcdfb9447faf474101662d0c846dfa25981
kate
  Wed Sep 4 17:23:18 2019 -0700
Interact track multi-region link now adds a custom track showing where multi-regions are located. refs #22422

diff --git src/hg/hgc/interactClick.c src/hg/hgc/interactClick.c
index 772c9ed..a69f1fd 100644
--- src/hg/hgc/interactClick.c
+++ src/hg/hgc/interactClick.c
@@ -1,25 +1,26 @@
 /* Details page for interact type tracks */
 
 /* Copyright (C) 2018 The Regents of the University of California 
  * See README in this or parent directory for licensing information. */
 
 #include "common.h"
 #include "obscure.h"
 #include "hdb.h"
 #include "jksql.h"
 #include "hgc.h"
+#include "customTrack.h"
 #include "trashDir.h"
 #include "hex.h"
 #include <openssl/sha.h>
 
 #include "interact.h"
 #include "interactUi.h"
 
 struct interactPlusRow
     {
     /* Keep field values in string format, for url processing */
     struct interactPlusRow *next;
     struct interact *interact;
     char **row;
     };
 
@@ -144,136 +145,163 @@
             if (differentString(name, inter->sourceName) && differentString(name, inter->targetName))
                 continue;
             }
         }
     minStart = inter->chromStart <  minStart ? inter->chromStart : minStart;
     maxEnd = inter->chromEnd > maxEnd ? inter->chromEnd : maxEnd;
     slAddHead(&filtered, ipr);
     }
 
 *retStart = minStart;
 *retEnd = maxEnd;
 // consider sorting on score or position
 return filtered;
 }
 
-static char *makeInteractRegionFile(char *name, struct interact *inters)
-/* Create bed file in trash directory with end coordinates for multi-region mode */
+static char *makeInteractRegionFile(char *name, struct interact *inters, char **retCustomText)
+/* Create bed file in trash directory with end coordinates for multi-region mode,
+ * and a custom track for display showing regions */
 {
 struct tempName mrTn;
 trashDirFile(&mrTn, "hgt", "custRgn_interact", ".bed");
 FILE *f = fopen(mrTn.forCgi, "w");
 if (f == NULL)
     errAbort("can't create temp file %s", mrTn.forCgi);
+
 char regionInfo[1024];
 int padding = 200;
 safef(regionInfo, sizeof regionInfo, "#padding %d\n", padding);
 mustWrite(f, regionInfo, strlen(regionInfo));
-//warn("%s", regionInfo);
 
 safef(regionInfo, sizeof regionInfo, "#shortDesc %s\n", name);
 mustWrite(f, regionInfo, strlen(regionInfo));
-//warn("%s", regionInfo);
 
 struct interact *inter = NULL;
 struct bed *region, *regions = NULL;
 struct hash *uniqRegions = hashNew(0);
+
+struct dyString *ds = dyStringNew(0);
+dyStringPrintf(ds, "track name='Multi Regions' description='Interact regions for %s' "
+                        "itemRgb=On labelOnFeature=on\n", name);
 for (inter = inters; inter != NULL; inter = inter->next)
     {
     char buf[256];
-    //safef(buf, sizeof buf, "%s:%d-%d", region1->chrom, region1->chromStart, region1->chromEnd);
     safef(buf, sizeof buf, "%s:%d-%d", inter->sourceChrom, inter->sourceStart, inter->sourceEnd);
     if (!hashLookup(uniqRegions, buf))
         {
         hashAdd(uniqRegions, cloneString(buf), NULL);
         AllocVar(region);
         region->chrom = inter->sourceChrom;
         region->chromStart = inter->sourceStart;
         region->chromEnd = inter->sourceEnd;
         slAddHead(&regions, region);
         }
     safef(buf, sizeof buf, "%s:%d-%d", inter->targetChrom, inter->targetStart, inter->targetEnd);
     if (!hashLookup(uniqRegions, buf))
         {
         hashAdd(uniqRegions, cloneString(buf), NULL);
         AllocVar(region);
         region->chrom = inter->targetChrom;
         region->chromStart = inter->targetStart;
         region->chromEnd = inter->targetEnd;
         slAddHead(&regions, region);
         }
     }
 slSort(&regions, bedCmp);
 struct bed *prevRegion = NULL;
+
+// alternate item colors
+char *colorLight = "184,201,255";
+char *colorDark = "0,0,0";
+boolean doLightColor = TRUE;
+
+// print regions to BED file and custom track
 for (region = regions; region != NULL; region = region->next)
     {
     // filter out nested regions
     if (prevRegion == NULL || differentString(region->chrom, prevRegion->chrom) ||
                 region->chromStart >=  prevRegion->chromEnd)
         {
         safef(regionInfo, sizeof regionInfo, "%s\t%d\t%d\n",
                     region->chrom, region->chromStart, region->chromEnd);
         mustWrite(f, regionInfo, strlen(regionInfo));
+        int start = max(region->chromStart - padding, 0);
+        int end = min(region->chromEnd + padding, hChromSize(database, region->chrom));
+        char *color = doLightColor ? colorLight : colorDark;
+        doLightColor = !doLightColor;
+        dyStringPrintf(ds, "%s\t%d\t%d\t"
+                                "%s:%d+%d_bp\t"
+                                "0\t.\t%d\t%d\t%s\n",
+                            region->chrom, start, end,
+                                region->chrom, start+1, end-start,
+                                start, end, color);
         }
     prevRegion = region;
     }
 fclose(f);
 
 // create SHA1 file; used to see if file has changed
 unsigned char hash[SHA_DIGEST_LENGTH];
 SHA1((const unsigned char *)regionInfo, strlen(regionInfo), hash);
 char newSha1[(SHA_DIGEST_LENGTH + 1) * 2];
 hexBinaryString(hash, SHA_DIGEST_LENGTH, newSha1, (SHA_DIGEST_LENGTH + 1) * 2);
 char sha1File[1024];
 safef(sha1File, sizeof sha1File, "%s.sha1", mrTn.forCgi);
 f = mustOpen(sha1File, "w");
 mustWrite(f, newSha1, strlen(newSha1));
 carefulClose(&f);
+
+// post custom track
+if (retCustomText != NULL)
+    *retCustomText = dyStringCannibalize(&ds);
+
+// return BED filename
 return cloneString(mrTn.forCgi);
 }
 
 static void multiRegionLink(char *name, struct interact *inters)
 // Print link to multi-region view of ends if appropriate 
 // (or provide a link to remove if already in this mode) 
 {
 if (inters->next == NULL && interactEndsOverlap(inters))
     return;
 char *virtShortDesc = cartOptionalString(cart, "virtShortDesc");
 boolean isVirtMode = cartUsualBoolean(cart, "virtMode", FALSE);
-//warn("virtShortDesc: %s, name: %s", virtShortDesc, name);
 if (isVirtMode && virtShortDesc && sameString(virtShortDesc, name))
     {
     printf("<br>Show interaction%s in "
                 "<a href='hgTracks?"
                     "virtMode=0&"
                     "virtModeType=default'>"
                 " normal browser view</a> (exit multi-region view)",
                         inters->next != NULL ? "s" : "");
     }
 else
     {
-    char *regionFile = makeInteractRegionFile(name, inters);
-    //warn("regionFile: %s", regionFile);
+    char *customText = NULL;
+    char *regionFile = makeInteractRegionFile(name, inters, &customText);
     printf("<br>Show interaction ends in "
             "<a href='hgTracks?"
                 "virtMode=1&"
                 "virtModeType=customUrl&"
                 "virtWinFull=on&"
                 "virtShortDesc=%s&"
-                "multiRegionsBedUrl=%s'>"
+                "multiRegionsBedUrl=%s&"
+                "%s=%s'>"
             " multi-region browser view </a>(custom region mode)",
-                    name, cgiEncode(regionFile));
+                    name, cgiEncode(regionFile),
+                    CT_CUSTOM_TEXT_VAR, cgiEncode(customText));
     if (isVirtMode)
         printf(" or "
                 "<a href='hgTracks?"
                     "virtMode=0&"
                     "virtModeType=default'>"
                 " normal browser view</a>");
     }
 printf("&nbsp;&nbsp;&nbsp;");
 printf("<a href=\"../goldenPath/help/multiRegionHelp.html\" target=_blank>(Help)</a>\n");
 }
 
 void doInteractRegionDetails(struct trackDb *tdb, struct interact *inter)
 {
     /* print info for both regions */
 /* Use different labels: