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