533112afe2a2005e80cdb1f82904ea65032d4302
braney
Sat Oct 2 11:37:34 2021 -0700
split hg/lib into two separate libaries, one only used by the cgis
diff --git src/hg/cgilib/genoLay.c src/hg/cgilib/genoLay.c
new file mode 100644
index 0000000..417d7b4
--- /dev/null
+++ src/hg/cgilib/genoLay.c
@@ -0,0 +1,564 @@
+/* genoLay - genome layout. Arranges chromosomes so that they
+ * tend to fit together nicely on a single page. */
+
+/* Copyright (C) 2014 The Regents of the University of California
+ * See README in this or parent directory for licensing information. */
+
+#include "common.h"
+#include "hash.h"
+#include "jksql.h"
+#include "vGfx.h"
+#include "cytoBand.h"
+#include "hCytoBand.h"
+#include "genoLay.h"
+
+
+void genoLayDump(struct genoLay *gl)
+/* Print out info on genoLay */
+{
+struct genoLayChrom *chrom;
+struct slRef *left, *right, *ref;
+int total;
+printf("gl: lineCount %d, leftLabelWidth %d, rightLabelWidth %d, basesPerPixel %f
\n",
+ gl->lineCount, gl->leftLabelWidth, gl->rightLabelWidth, gl->basesPerPixel);
+for (left = gl->leftList, right = gl->rightList; left != NULL || right != NULL;)
+ {
+ total=0;
+ if (left != NULL)
+ {
+ chrom = left->val;
+ printf("%s@%d,%d[%d] %d ---- ", chrom->fullName, chrom->x, chrom->y, chrom->width, chrom->size);
+ total += chrom->size;
+ left = left->next;
+ }
+ if (right != NULL)
+ {
+ chrom = right->val;
+ printf("%d %s@%d,%d[%d]", chrom->size, chrom->fullName, chrom->x, chrom->y, chrom->width);
+ total += chrom->size;
+ right = right->next;
+ }
+ printf(" : %d
", total);
+ }
+total=0;
+for (ref = gl->bottomList; ref != NULL; ref = ref->next)
+ {
+ chrom = ref->val;
+ total += chrom->size;
+ printf("%s@%d,%d[%d] %d ... ", chrom->fullName, chrom->x, chrom->y, chrom->width, chrom->size);
+ }
+printf(" : %d
", total);
+}
+
+int genoLayChromCmpName(const void *va, const void *vb)
+/* Compare two chromosome names so as to sort numerical part
+ * by number.. */
+{
+const struct genoLayChrom *a = *((struct genoLayChrom **)va);
+const struct genoLayChrom *b = *((struct genoLayChrom **)vb);
+char *aName = a->shortName, *bName = b->shortName;
+if (isdigit(aName[0]))
+ {
+ if (isdigit(bName[0]))
+ {
+ int diff = atoi(aName) - atoi(bName);
+ if (diff == 0)
+ diff = strcmp(skipNumeric(aName), skipNumeric(bName));
+ return diff;
+ }
+ else
+ return -1;
+ }
+else if (isdigit(bName[0]))
+ return 1;
+else
+ return strcmp(aName, bName);
+}
+
+struct genoLayChrom *genoLayDbChromsExt(struct sqlConnection *conn,
+ boolean withRandom, boolean abortOnErr)
+/* Get chrom info list. */
+{
+struct sqlResult *sr;
+char **row;
+struct genoLayChrom *chrom, *chromList = NULL;
+sr = sqlGetResult(conn, NOSQLINJ "select chrom,size from chromInfo");
+while ((row = sqlNextRow(sr)) != NULL)
+ {
+ char *name = row[0];
+ if (withRandom ||
+ (
+ (startsWith("chr", name))
+ && (!startsWith("chrUn", name))
+ && (!sameString("chrM", name))
+ && (!strchr(name, '_')) // avoiding _random and _hap*
+ )
+ )
+ {
+ AllocVar(chrom);
+ chrom->fullName = cloneString(name);
+ chrom->shortName = chrom->fullName+3;
+ chrom->size = sqlUnsigned(row[1]);
+ slAddHead(&chromList, chrom);
+ }
+ }
+if (chromList == NULL)
+ {
+ if (abortOnErr)
+ errAbort("No chromosomes found in chromInfo.");
+ return NULL;
+ }
+int count = slCount(chromList);
+if (count > 500)
+ {
+ if (abortOnErr)
+ errAbort("Sorry, cannot do genome layout on an assembly "
+ "with too many (%d) chromosomes exceeds 500. "
+ "Please select another organism or assembly.", count);
+ }
+slReverse(&chromList);
+slSort(&chromList, genoLayChromCmpName);
+return chromList;
+}
+
+struct genoLayChrom *genoLayDbChroms(struct sqlConnection *conn,
+ boolean withRandom)
+/* Get chrom info list. */
+{
+return genoLayDbChromsExt(conn, withRandom, TRUE);
+}
+
+
+static void separateSexChroms(struct slRef *in,
+ struct slRef **retAutoList, struct slRef **retSexList)
+/* Separate input chromosome list into sex and non-sex chromosomes. */
+{
+struct slRef *autoList = NULL, *sexList = NULL, *ref, *next;
+
+for (ref = in; ref != NULL; ref = next)
+ {
+ struct genoLayChrom *chrom = ref->val;
+ char *name = chrom->shortName;
+ next = ref->next;
+ if (sameWord(name, "X") || sameWord(name, "Y") || sameWord(name, "Z")
+ || sameWord(name, "W"))
+ {
+ slAddHead(&sexList, ref);
+ }
+ else
+ {
+ slAddHead(&autoList, ref);
+ }
+ }
+slReverse(&sexList);
+slReverse(&autoList);
+*retAutoList = autoList;
+*retSexList = sexList;
+}
+
+struct genoLay *genoLayNew(struct genoLayChrom *chromList,
+ MgFont *font, int picWidth, int betweenChromHeight,
+ int minLeftLabelWidth, int minRightLabelWidth,
+ char *how)
+/* Figure out layout. For human and most mammals this will be
+ * two columns with sex chromosomes on bottom. This is complicated
+ * by the platypus having a bunch of sex chromosomes. */
+{
+int margin = 3;
+struct slRef *refList = NULL, *ref, *left, *right;
+struct genoLayChrom *chrom;
+struct genoLay *gl;
+int autoCount, halfCount, bases;
+int leftLabelWidth=0, rightLabelWidth=0, labelWidth;
+int spaceWidth = mgFontCharWidth(font, ' ');
+int extraLabelPadding = 0;
+int autosomeOtherPixels=0, sexOtherPixels=0;
+int autosomeBasesInLine=0; /* Maximum bases in a line for autosome. */
+int sexBasesInLine=0; /* Bases in line for sex chromsome. */
+double sexBasesPerPixel, autosomeBasesPerPixel, basesPerPixel;
+int pos = margin;
+int y = 0;
+int fontHeight = mgFontLineHeight(font);
+int chromHeight = fontHeight;
+int lineHeight = chromHeight + betweenChromHeight;
+boolean allOneLine = FALSE;
+
+refList = refListFromSlList(chromList);
+
+/* Allocate genoLay object and fill in simple fields. */
+AllocVar(gl);
+gl->chromList = chromList;
+gl->chromHash = hashNew(0);
+gl->font = font;
+gl->picWidth = picWidth;
+gl->margin = margin;
+gl->spaceWidth = spaceWidth;
+gl->lineHeight = lineHeight;
+gl->betweenChromHeight = betweenChromHeight;
+gl->betweenChromOffsetY = 0;
+gl->chromHeight = chromHeight;
+gl->chromOffsetY = lineHeight - chromHeight;
+
+
+/* Save chromosomes in hash too, for easy access */
+for (chrom = chromList; chrom != NULL; chrom = chrom->next)
+ hashAdd(gl->chromHash, chrom->fullName, chrom);
+
+if (sameString(how, genoLayOnePerLine))
+ {
+ gl->leftList = refList;
+ }
+else if (sameString(how, genoLayAllOneLine))
+ {
+ gl->bottomList = refList;
+ allOneLine = TRUE;
+ }
+else
+ {
+ /* Put sex chromosomes on bottom, and rest on left. */
+ separateSexChroms(refList, &refList, &gl->bottomList);
+ autoCount = slCount(refList);
+ gl->leftList = refList;
+
+ /* If there are a lot of chromosomes, then move later
+ * (and smaller) chromosomes to a new right column */
+ if (autoCount > 12)
+ {
+ halfCount = (autoCount+1)/2;
+ ref = slElementFromIx(refList, halfCount-1);
+ gl->rightList = ref->next;
+ ref->next = NULL;
+ slReverse(&gl->rightList);
+ }
+ }
+
+if (allOneLine)
+ {
+ unsigned long totalBases = 0, bStart=0, bEnd;
+ int chromCount = 0, chromIx=0;
+ for (ref = gl->bottomList; ref != NULL; ref = ref->next)
+ {
+ chrom = ref->val;
+ totalBases += chrom->size;
+ chromCount += 1;
+ }
+ int availablePixels = picWidth - minLeftLabelWidth - minRightLabelWidth
+ - 2*margin - (chromCount-1);
+ double basesPerPixel = (double)totalBases/availablePixels;
+ gl->picHeight = 2*margin + lineHeight + fontHeight;
+ for (ref = gl->bottomList; ref != NULL; ref = ref->next)
+ {
+ chrom = ref->val;
+ bEnd = bStart + chrom->size;
+ int pixStart = round(bStart / basesPerPixel);
+ int pixEnd = round(bEnd / basesPerPixel);
+ chrom->width = pixEnd - pixStart;
+ chrom->height = lineHeight;
+ chrom->x = pixStart + margin + chromIx + minLeftLabelWidth;
+ chrom->y = 0;
+ chromIx += 1;
+ bStart = bEnd;
+ }
+ gl->lineCount = 1;
+ gl->picHeight = 2*margin + lineHeight + fontHeight + 1;
+ gl->allOneLine = TRUE;
+ gl->leftLabelWidth = minLeftLabelWidth;
+ gl->rightLabelWidth = minRightLabelWidth;
+ gl->basesPerPixel = basesPerPixel;
+ gl->pixelsPerBase = 1.0/basesPerPixel;
+ }
+else
+ {
+ /* Figure out space needed for autosomes. */
+ left = gl->leftList;
+ right = gl->rightList;
+ while (left || right)
+ {
+ bases = 0;
+ if (left)
+ {
+ chrom = left->val;
+ labelWidth = mgFontStringWidth(font, chrom->shortName) + spaceWidth;
+ if (leftLabelWidth < labelWidth)
+ leftLabelWidth = labelWidth;
+ bases = chrom->size;
+ left = left->next;
+ }
+ if (right)
+ {
+ chrom = right->val;
+ labelWidth = mgFontStringWidth(font, chrom->shortName) + spaceWidth;
+ if (rightLabelWidth < labelWidth)
+ rightLabelWidth = labelWidth;
+ bases += chrom->size;
+ right = right->next;
+ }
+ if (autosomeBasesInLine < bases)
+ autosomeBasesInLine = bases;
+ gl->lineCount += 1;
+ }
+
+ /* Figure out space needed for bottom chromosomes. */
+ if (gl->bottomList)
+ {
+ gl->lineCount += 1;
+ sexOtherPixels = spaceWidth + 2*margin;
+ for (ref = gl->bottomList; ref != NULL; ref = ref->next)
+ {
+ chrom = ref->val;
+ sexBasesInLine += chrom->size;
+ labelWidth = mgFontStringWidth(font, chrom->shortName) + spaceWidth;
+ if (ref == gl->bottomList )
+ {
+ if (leftLabelWidth < labelWidth)
+ leftLabelWidth = labelWidth;
+ sexOtherPixels = leftLabelWidth;
+ }
+ else if (ref->next == NULL)
+ {
+ if (rightLabelWidth < labelWidth)
+ rightLabelWidth = labelWidth;
+ sexOtherPixels += rightLabelWidth + spaceWidth;
+ }
+ else
+ {
+ sexOtherPixels += labelWidth + spaceWidth;
+ }
+ }
+ }
+
+ /* Do some adjustments if side labels are bigger than needed for
+ * chromosome names. */
+ if (leftLabelWidth < minLeftLabelWidth)
+ {
+ extraLabelPadding += (minLeftLabelWidth - leftLabelWidth);
+ leftLabelWidth = minLeftLabelWidth;
+ }
+ if (rightLabelWidth < minRightLabelWidth)
+ {
+ extraLabelPadding += (minRightLabelWidth - rightLabelWidth);
+ rightLabelWidth = minRightLabelWidth;
+ }
+ sexOtherPixels += extraLabelPadding;
+
+ /* Figure out the number of bases needed per pixel. */
+ autosomeOtherPixels = 2*margin + spaceWidth + leftLabelWidth + rightLabelWidth;
+ basesPerPixel = autosomeBasesPerPixel
+ = autosomeBasesInLine/(picWidth-autosomeOtherPixels);
+ if (gl->bottomList)
+ {
+ sexBasesPerPixel = sexBasesInLine/(picWidth-sexOtherPixels);
+ if (sexBasesPerPixel > basesPerPixel)
+ basesPerPixel = sexBasesPerPixel;
+ }
+
+ /* Save positions and sizes of some things in layout structure. */
+ gl->leftLabelWidth = leftLabelWidth;
+ gl->rightLabelWidth = rightLabelWidth;
+ gl->basesPerPixel = basesPerPixel;
+ gl->pixelsPerBase = 1.0/basesPerPixel;
+
+ /* Set pixel positions for left autosomes */
+ for (ref = gl->leftList; ref != NULL; ref = ref->next)
+ {
+ chrom = ref->val;
+ chrom->x = leftLabelWidth + margin;
+ chrom->y = y;
+ chrom->width = round(chrom->size/basesPerPixel);
+ chrom->height = lineHeight;
+ y += lineHeight;
+ }
+
+ /* Set pixel positions for right autosomes */
+ y = 0;
+ for (ref = gl->rightList; ref != NULL; ref = ref->next)
+ {
+ chrom = ref->val;
+ chrom->width = round(chrom->size/basesPerPixel);
+ chrom->height = lineHeight;
+ chrom->x = picWidth - margin - rightLabelWidth - chrom->width;
+ chrom->y = y;
+ y += lineHeight;
+ }
+ gl->picHeight = 2*margin + lineHeight * gl->lineCount;
+ y = gl->picHeight - margin - lineHeight;
+
+ /* Set pixel positions for sex chromosomes */
+ for (ref = gl->bottomList; ref != NULL; ref = ref->next)
+ {
+ chrom = ref->val;
+ chrom->y = y;
+ chrom->width = round(chrom->size/basesPerPixel);
+ chrom->height = lineHeight;
+ if (ref == gl->bottomList)
+ chrom->x = leftLabelWidth + margin;
+ else if (ref->next == NULL)
+ chrom->x = picWidth - margin - rightLabelWidth - chrom->width;
+ else
+ chrom->x = 2*spaceWidth+mgFontStringWidth(font,chrom->shortName) + pos;
+ pos = chrom->x + chrom->width;
+ }
+ }
+return gl;
+}
+
+static void leftLabel(struct hvGfx *hvg, struct genoLay *gl,
+ struct genoLayChrom *chrom, int yOffset, int fontHeight,
+ int color)
+/* Draw a chromosome with label on left. */
+{
+hvGfxTextRight(hvg, gl->margin, chrom->y + yOffset,
+ chrom->x - gl->margin - gl->spaceWidth, fontHeight, color,
+ gl->font, chrom->shortName);
+}
+
+static void rightLabel(struct hvGfx *hvg, struct genoLay *gl,
+ struct genoLayChrom *chrom, int yOffset, int fontHeight,
+ int color)
+/* Draw a chromosome with label on left. */
+{
+hvGfxText(hvg, chrom->x + chrom->width + gl->spaceWidth,
+ chrom->y + yOffset,
+ color, gl->font, chrom->shortName);
+}
+
+static void midLabel(struct hvGfx *hvg, struct genoLay *gl,
+ struct genoLayChrom *chrom, int yOffset, int fontHeight,
+ int color)
+/* Draw a chromosome with label on left. */
+{
+MgFont *font = gl->font;
+int textWidth = mgFontStringWidth(font, chrom->shortName);
+hvGfxTextRight(hvg, chrom->x - textWidth - gl->spaceWidth,
+ chrom->y + yOffset,
+ textWidth, fontHeight, color,
+ font, chrom->shortName);
+}
+
+void genoLayDrawChromLabels(struct genoLay *gl, struct hvGfx *hvg, int color)
+/* Draw chromosomes labels in image */
+{
+struct slRef *ref;
+struct genoLayChrom *chrom;
+int pixelHeight = mgFontPixelHeight(gl->font);
+if (gl->allOneLine)
+ {
+ int yOffset = gl->chromOffsetY + gl->chromHeight + 1;
+
+ for (ref = gl->bottomList; ref != NULL; ref = ref->next)
+ {
+ chrom = ref->val;
+ hvGfxTextCentered(hvg, chrom->x, yOffset, chrom->width, pixelHeight, color, gl->font,
+ chrom->shortName);
+ }
+ }
+else
+ {
+ int yOffset = gl->chromOffsetY + gl->chromHeight - pixelHeight;
+
+ /* Draw chromosome labels. */
+ for (ref = gl->leftList; ref != NULL; ref = ref->next)
+ leftLabel(hvg, gl, ref->val, yOffset, pixelHeight, color);
+ for (ref = gl->rightList; ref != NULL; ref = ref->next)
+ rightLabel(hvg, gl, ref->val, yOffset, pixelHeight, color);
+ for (ref = gl->bottomList; ref != NULL; ref = ref->next)
+ {
+ chrom = ref->val;
+ if (ref == gl->bottomList)
+ leftLabel(hvg, gl, chrom, yOffset, pixelHeight, color);
+ else if (ref->next == NULL)
+ rightLabel(hvg, gl, chrom, yOffset, pixelHeight, color);
+ else
+ midLabel(hvg, gl, chrom, yOffset, pixelHeight, color);
+ }
+ }
+}
+
+void genoLayDrawSimpleChroms(struct genoLay *gl,
+ struct hvGfx *hvg, int color)
+/* Draw boxes for all chromosomes in given color */
+{
+int height = gl->chromHeight;
+int yOffset = gl->chromOffsetY;
+struct genoLayChrom *chrom;
+for (chrom = gl->chromList; chrom != NULL; chrom = chrom->next)
+ hvGfxBox(hvg, chrom->x, chrom->y + yOffset,
+ chrom->width, height, color);
+
+}
+
+void genoLayDrawBandedChroms(struct genoLay *gl, struct hvGfx *hvg, char *db,
+ struct sqlConnection *conn, Color *shadesOfGray, int maxShade,
+ int defaultColor)
+/* Draw chromosomes with centromere and band glyphs.
+ * Get the band data from the database. If the data isn't
+ * there then draw simple chroms in default color instead */
+{
+char *bandTable = "cytoBandIdeo";
+int yOffset = gl->chromOffsetY;
+genoLayDrawSimpleChroms(gl, hvg, defaultColor);
+if (sqlTableExists(conn, bandTable) && !gl->allOneLine)
+ {
+ int centromereColor = hCytoBandCentromereColor(hvg);
+ double pixelsPerBase = 1.0/gl->basesPerPixel;
+ int height = gl->chromHeight;
+ int innerHeight = gl->chromHeight-2;
+ struct genoLayChrom *chrom;
+ boolean isDmel = hCytoBandDbIsDmel(db);
+ boolean bColor = hvGfxFindColorIx(hvg, 200, 150, 150);
+ int fontPixelHeight = mgFontPixelHeight(gl->font);
+ for (chrom = gl->chromList; chrom != NULL; chrom = chrom->next)
+ {
+ struct sqlResult *sr;
+ char **row;
+ char query[256];
+ int cenX1=BIGNUM, cenX2=0;
+ int y = chrom->y + yOffset;
+
+ /* Fetch bands from database and draw them. */
+ sqlSafef(query, sizeof(query), "select * from %s where chrom='%s'",
+ bandTable, chrom->fullName);
+ sr = sqlGetResult(conn, query);
+ while ((row = sqlNextRow(sr)) != NULL)
+ {
+ struct cytoBand band;
+ int x1, x2;
+ cytoBandStaticLoad(row, &band);
+ x1 = pixelsPerBase*band.chromStart;
+ x2 = pixelsPerBase*band.chromEnd;
+ if (sameString(band.gieStain, "acen"))
+ {
+ /* Centromere is represented as two adjacent bands.
+ * We'll just record the extents of it here, and draw it
+ * in one piece later. */
+ if (x1 < cenX1)
+ cenX1 = x1;
+ if (x2 > cenX2)
+ cenX2 = x2;
+ }
+ else
+ {
+ /* Draw band */
+ hCytoBandDrawAt(&band, hvg, x1+chrom->x, y+1, x2-x1, innerHeight,
+ isDmel, gl->font, fontPixelHeight, MG_BLACK, bColor,
+ shadesOfGray, maxShade);
+ }
+ }
+ sqlFreeResult(&sr);
+
+ /* Draw box around chromosome */
+ hvGfxBox(hvg, chrom->x, y, chrom->width, 1, MG_BLACK);
+ hvGfxBox(hvg, chrom->x, y+height-1, chrom->width, 1, MG_BLACK);
+ hvGfxBox(hvg, chrom->x, y, 1, height, MG_BLACK);
+ hvGfxBox(hvg, chrom->x+chrom->width-1, y, 1, height, MG_BLACK);
+
+ /* Draw centromere if we found one. */
+ if (cenX2 > cenX1)
+ {
+ hCytoBandDrawCentromere(hvg, cenX1+chrom->x, y, cenX2-cenX1, height,
+ MG_WHITE, centromereColor);
+ }
+ }
+ }
+}
+