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); + } + } + } +} +