1 ###########################################################################
3 # C O P Y R I G H T N O T I C E #
4 # Copyright (c) 2003-10 by: #
5 # * California Institute of Technology #
7 # All Rights Reserved. #
9 # Permission is hereby granted, free of charge, to any person #
10 # obtaining a copy of this software and associated documentation files #
11 # (the "Software"), to deal in the Software without restriction, #
12 # including without limitation the rights to use, copy, modify, merge, #
13 # publish, distribute, sublicense, and/or sell copies of the Software, #
14 # and to permit persons to whom the Software is furnished to do so, #
15 # subject to the following conditions: #
17 # The above copyright notice and this permission notice shall be #
18 # included in all copies or substantial portions of the Software. #
20 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, #
21 # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF #
22 # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND #
23 # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS #
24 # BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN #
25 # ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN #
26 # CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE #
28 ###########################################################################
30 # This class contains the core code for visualizing sequences from several of the experiments.
32 from cistematic.core.motif import Motif
33 from cistematic.core.geneinfo import geneinfoDB
39 import Image, ImageDraw, ImageFont
46 """ The Draw class contains the code used to visualize the location of motifs in their
47 genomic neighborhood. It is meant to be used as the parent of other classes,
48 such as the orthology classes. It relies on the python imaging library, and saves
58 theFont = ImageFont.load_default()
61 print "Draw: python image library missing - will not be able to draw on this system"
64 def draw(self, picName, geneList=[], motifList=[], excludeGeneList=[], excludeMotifList=[],
65 showHeader=True, showFooter=True, maxOccurences=100, skipSanity=False):
66 """ Draws an image of motifs on the sequences. Can specifically list or exclude genes and/or motifs.
67 Options showHeader will add experiment information while showFooter will add a motif key.
68 This function will not show motifs that occur more than maxOccurences in the dataset.
78 if len(motifList) > 0:
83 if self.boundToNextGene:
86 expResults = self.getResults()
87 (up, cds, down) = self.getSeqParameters()
100 adjustedMotLength = {}
101 datasetIDs = self.getDatasetNames()
103 if len(datasetIDs) > 0:
104 for datasetName in datasetIDs:
105 dataset = self.getSetting(datasetName)
106 theList = eval(dataset[0])
108 if limitGenes and gene not in geneList:
111 if gene not in excludeGeneList and gene not in orthologyList:
112 orthologyList.append(gene)
114 for gene in self.genepool:
115 if limitGenes and gene not in geneList:
118 if gene not in excludeGeneList and gene not in orthologyList:
119 orthologyList.append(gene)
121 for geneID in orthologyList:
122 geneLength[geneID] = len(self.genepool[geneID])
123 if maxLength < geneLength[geneID]:
124 maxLength = geneLength[geneID]
126 seqScaler = float(self.maxWidth) / float(maxLength)
127 for mot in expResults:
128 if limitMotifs and mot.tagID not in motifList:
131 if mot.tagID in excludeMotifList:
134 if len(self.motifToGene(mot.tagID)) >= maxOccurences:
137 if not skipSanity and not mot.isSane():
140 motConsensus[mot.tagID] = mot.buildConsensus()
141 currentRed = random.randint(5, 240)
142 currentGreen = random.randint(5, 240)
143 currentBlue = random.randint(5, 240)
144 motColor[mot.tagID] = (currentRed, currentGreen, currentBlue)
146 motKeys = motConsensus.keys()
148 motKeysLen = len(motKeys)
149 for tagID in motKeys:
151 adjustedMotLength[tagID] = int(math.ceil(len(self.findMotif(tagID)) * seqScaler))
153 numLines = len(orthologyList)
157 footerLines = motKeysLen / 3
161 imsize = (self.maxWidth + self.leftMargin + self.rightMargin, int(round((numLines + footerLines/2.) * self.lineHeight)))
162 image = Image.new("RGB", imsize, color="#ffffff")
163 draw = ImageDraw.Draw(image)
166 line1 = "Experiment: %s in %s Type: %s Analysis: %s" % (self.experimentID, self.expFile, self.experimentType, self.analysisID)
167 draw.text([10, 10], line1, font=self.theFont, fill=0)
168 draw.line((10, 30, self.maxWidth + self.leftMargin + self.rightMargin - 10, 30), fill=0)
169 line2 = "Upstream: %s%s ORF: %s Downstream: %s%s" % (bound, up, hasORF, bound, down)
170 draw.text([10, 40], line2, font=self.theFont, fill=0)
171 draw.line((10, 60, self.maxWidth + self.leftMargin + self.rightMargin - 10, 60), fill=0)
172 currentHeight = self.lineHeight
174 for geneID in orthologyList:
176 seqLength = geneLength[geneID]
178 res = idb.geneIDSynonyms(geneID)
179 for entry in res[1:]:
180 geneNames += "%s " % str(entry)
184 motList = self.geneToMotif(geneID)
185 draw.text([5, currentHeight+45], str(geneID[0]), font=self.theFont, fill=0)
186 draw.text([5, currentHeight+55], str(geneID[1]), font=self.theFont, fill=0)
187 draw.text([5, currentHeight+65], geneNames, font=self.theFont, fill=0)
188 adjustedSeqLength = seqLength * seqScaler
189 seqStart = self.leftMargin + int(self.maxWidth) - adjustedSeqLength
190 features = self.getFeatures(geneID)
191 for (ftype, fstart, fstop, forientation) in features:
195 if float(fstop) < float(fstart):
199 start = int(math.floor(float(fstart) * seqScaler))
200 consLength = int(math.ceil((fstop - fstart) * seqScaler))
201 if start + consLength > adjustedSeqLength:
202 consLength = adjustedSeqLength - start
205 draw.rectangle([start, currentHeight + 51, start + consLength, currentHeight + 64], fill="#aaaaaa")
207 draw.rectangle([seqStart, currentHeight + 35, seqStart + adjustedSeqLength, currentHeight + 65], outline=0)
210 for (tagID, (pos, sense)) in motList:
211 if tagID not in motKeys:
215 motNumber[tagID] += 1
219 start = int(math.floor(float(pos) * seqScaler)) + seqStart
234 for (prevStart, prevHeight) in tagPosList:
235 if abs(prevStart - start) <= 7 and abs(prevHeight - textHeight) <= 7:
241 tagPosList.append((start, textHeight))
242 draw.rectangle([start, currentHeight + top, start + adjustedMotLength[tagID], currentHeight + 65 + bottom], fill=motColor[tagID])
243 if tagID.count("-") == 2:
244 tagIDlist = tagID.split("-")
245 tempID = tagIDlist[0] + tagIDlist[1][0] + tagIDlist[2]
249 draw.text([start - 5, currentHeight + textHeight], tempID, font=self.theFont, fill=motColor[tagID])
251 conservedWindows = []
253 conservedWindows = self.getConservedSequenceWindows(geneID)
257 for (location, cLength, criteria) in conservedWindows:
258 start = int(math.floor(float(location) * seqScaler)) + seqStart
259 consLength = int(math.ceil(float(cLength) * seqScaler))
260 draw.rectangle([start, currentHeight + 38, start + consLength, currentHeight + 50], fill ='#ff0000')
262 for location in range(0, seqLength, 1000):
263 start = int(math.floor(float(location) * seqScaler)) + seqStart
264 draw.rectangle([start, currentHeight + 60, start + 1, currentHeight + 65], fill = '#000000')
266 draw.text([self.leftMargin + self.maxWidth + 5, currentHeight + 45], str(seqLength), font=self.theFont, fill=0)
267 currentHeight += self.lineHeight
271 for motID in motKeys:
272 x = self.leftMargin + (self.maxWidth / 3) * (motNum % 3)
273 x = self.leftMargin + (self.maxWidth / 3) * (motNum % 3)
274 y1 = currentHeight + ((self.lineHeight/2) * (motNum/3))+ 5
275 y2 = currentHeight + ((self.lineHeight/2) * (motNum/3))+ 15
276 y3 = currentHeight + ((self.lineHeight/2) * (motNum/3))+ 25
277 draw.text([x, y1], motID, font=self.theFont, fill=motColor[motID])
278 draw.text([x, y2], motConsensus[motID], font=self.theFont, fill=0)
279 draw.text([x, y3], str(motNumber[motID]) + " matches", font=self.theFont, fill=0)
283 image.save(picName, "PNG")
286 def drawMotifs(self, picName, motifList, geneList=[], excludeGeneList=[], showHeader=True,
287 showFooter=True, maxOccurences=100, genesWithMotifOnly=True, skipSanity=False):
288 """ Draws an image of one or more motifs on the sequences. Can specifically list or exclude genes.
289 Options showHeader will add experiment information while showFooter will add a motif key.
290 This function will not show motifs that occur more than maxOccurences in the dataset, and will
291 only show sequences with the motif by default.
293 restrictedGeneList = []
294 if genesWithMotifOnly:
295 for motID in motifList:
296 matches = self.motifToGene(motID)
297 for (loc, pos) in matches:
298 if loc not in restrictedGeneList and loc not in excludeGeneList:
299 restrictedGeneList.append(loc)
301 restrictedGeneList = geneList
303 self.draw(picName, restrictedGeneList, motifList, excludeGeneList, [], showHeader, showFooter, maxOccurences, skipSanity)
306 def drawGenes(self, picName, geneList, motifList=[], excludeMotifList=[], showHeader=True,
307 showFooter=True, maxOccurences=100, includeHomologs=False, motifsOnGeneOnly=True,
309 """ Draws an image of one or more motifs on the sequences. Can specifically list or exclude motif.
310 Options showHeader will add experiment information while showFooter will add a motif key.
311 This function will not show motifs that occur more than maxOccurences in the dataset, and will
312 only show motifs on the sequence by default in the footer.
314 restrictedMotifList = []
317 for geneID in geneList:
318 if geneID not in theGeneList:
319 theGeneList.append(geneID)
321 hgenes = self.returnHomologs(geneID)
323 if gID in self.genepool and gID not in theGeneList:
324 theGeneList.append(gID)
328 theGeneList = geneList
331 for geneID in theGeneList:
332 matches = self.geneToMotif(geneID)
333 for (motID, pos) in matches:
334 if motID not in restrictedMotifList and motID not in excludeMotifList:
335 restrictedMotifList.append(motID)
337 restrictedMotifList = motifList
339 self.draw(picName, theGeneList, restrictedMotifList, [], excludeMotifList, showHeader, showFooter, maxOccurences, skipSanity)