e70152e44cc66cc599ff6b699eb8adc07f3e656a
kent
Sat May 24 21:09:34 2014 -0700
Adding Copyright NNNN Regents of the University of California to all files I believe with reasonable certainty were developed under UCSC employ or as part of Genome Browser copyright assignment.
diff --git src/hg/lib/genoLay.c src/hg/lib/genoLay.c
index fbdd858..9de5cd5 100644
--- src/hg/lib/genoLay.c
+++ src/hg/lib/genoLay.c
@@ -1,564 +1,567 @@
/* 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, chromInLine;
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;
chromInLine = 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)
{
boolean gotAny = FALSE;
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);
gotAny = TRUE;
}
}
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);
}
}
}
}