1
2
3
4
5
6
7 """Cluster tree visualization using Sping
8
9 """
10
11 try:
12 from sping import pid
13 piddle = pid
14 except ImportError:
15 from piddle import piddle
16 import ClusterUtils
17
18 import numpy
19
21 """ stores visualization options for cluster viewing
22
23 **Instance variables**
24
25 - x/yOffset: amount by which the drawing is offset from the edges of the canvas
26
27 - lineColor: default color for drawing the cluster tree
28
29 - lineWidth: the width of the lines used to draw the tree
30
31 """
32 xOffset = 20
33 yOffset = 20
34 lineColor = piddle.Color(0,0,0)
35 hideColor = piddle.Color(.8,.8,.8)
36 terminalColors = [piddle.Color(1,0,0),piddle.Color(0,0,1),piddle.Color(1,1,0),
37 piddle.Color(0,.5,.5),piddle.Color(0,.8,0),piddle.Color(.5,.5,.5),
38 piddle.Color(.8,.3,.3),piddle.Color(.3,.3,.8),piddle.Color(.8,.8,.3),
39 piddle.Color(.3,.8,.8)]
40 lineWidth = 2
41 hideWidth = 1.1
42 nodeRad=15
43 nodeColor = piddle.Color(1.,.4,.4)
44 highlightColor = piddle.Color(1.,1.,.4)
45 highlightRad = 10
46
48 val = float(val)
49 nval = pow(val,power)
50 if nval < min:
51 return 0.0
52 else:
53 return numpy.log(nval/min)
54
56 - def __init__(self,canvas,size,
57 ptColors=[],lineWidth=None,
58 showIndices=0,
59 showNodes=1,
60 stopAtCentroids=0,
61 logScale=0,
62 tooClose=-1):
63 self.canvas = canvas
64 self.size = size
65 self.ptColors = ptColors
66 self.lineWidth = lineWidth
67 self.showIndices = showIndices
68 self.showNodes = showNodes
69 self.stopAtCentroids = stopAtCentroids
70 self.logScale = logScale
71 self.tooClose = tooClose
72
86
112
114 """
115 we assume that _drawPos settings have been done already
116 """
117 if self.lineWidth is None:
118 lineWidth = VisOpts.lineWidth
119 else:
120 lineWidth = self.lineWidth
121
122 examine = [cluster]
123 while len(examine):
124 node = examine.pop(0)
125 xp,yp = node._drawPos
126 children = node.GetChildren()
127 if abs(children[1]._drawPos[0]-children[0]._drawPos[0])>self.tooClose:
128
129 drawColor = VisOpts.lineColor
130 self.canvas.drawLine(children[0]._drawPos[0],yp,
131 children[-1]._drawPos[0],yp,
132 drawColor,lineWidth)
133
134 for child in children:
135 if self.ptColors and child.GetData() is not None:
136 drawColor = self.ptColors[child.GetData()]
137 else:
138 drawColor = VisOpts.lineColor
139 cxp,cyp = child._drawPos
140 self.canvas.drawLine(cxp,yp,cxp,cyp,drawColor,lineWidth)
141 if not child.IsTerminal():
142 examine.append(child)
143 else:
144 if self.showIndices and not self.stopAtCentroids:
145 try:
146 txt = str(child.GetName())
147 except:
148 txt = str(child.GetIndex())
149 self.canvas.drawString(txt,
150 cxp-self.canvas.stringWidth(txt)/2,
151 cyp)
152
153 else:
154
155 self.canvas.drawLine(xp,yp,xp,self.size[1]-VisOpts.yOffset,
156 VisOpts.hideColor,lineWidth)
157
158
159 - def DrawTree(self,cluster,minHeight=2.0):
174
175
176 -def DrawClusterTree(cluster,canvas,size,
177 ptColors=[],lineWidth=None,
178 showIndices=0,
179 showNodes=1,
180 stopAtCentroids=0,
181 logScale=0,
182 tooClose=-1):
183 """ handles the work of drawing a cluster tree on a Sping canvas
184
185 **Arguments**
186
187 - cluster: the cluster tree to be drawn
188
189 - canvas: the Sping canvas on which to draw
190
191 - size: the size of _canvas_
192
193 - ptColors: if this is specified, the _colors_ will be used to color
194 the terminal nodes of the cluster tree. (color == _pid.Color_)
195
196 - lineWidth: if specified, it will be used for the widths of the lines
197 used to draw the tree
198
199 **Notes**
200
201 - _Canvas_ is neither _save_d nor _flush_ed at the end of this
202
203 - if _ptColors_ is the wrong length for the number of possible terminal
204 node types, this will throw an IndexError
205
206 - terminal node types are determined using their _GetData()_ methods
207
208 """
209 renderer = ClusterRenderer(canvas,size,ptColors,lineWidth,showIndices,showNodes,stopAtCentroids,
210 logScale,tooClose)
211 renderer.DrawTree(cluster)
212 -def _DrawClusterTree(cluster,canvas,size,
213 ptColors=[],lineWidth=None,
214 showIndices=0,
215 showNodes=1,
216 stopAtCentroids=0,
217 logScale=0,
218 tooClose=-1):
219 """ handles the work of drawing a cluster tree on a Sping canvas
220
221 **Arguments**
222
223 - cluster: the cluster tree to be drawn
224
225 - canvas: the Sping canvas on which to draw
226
227 - size: the size of _canvas_
228
229 - ptColors: if this is specified, the _colors_ will be used to color
230 the terminal nodes of the cluster tree. (color == _pid.Color_)
231
232 - lineWidth: if specified, it will be used for the widths of the lines
233 used to draw the tree
234
235 **Notes**
236
237 - _Canvas_ is neither _save_d nor _flush_ed at the end of this
238
239 - if _ptColors_ is the wrong length for the number of possible terminal
240 node types, this will throw an IndexError
241
242 - terminal node types are determined using their _GetData()_ methods
243
244 """
245 if lineWidth is None:
246 lineWidth = VisOpts.lineWidth
247 pts = cluster.GetPoints()
248 nPts = len(pts)
249 if nPts <= 1: return
250 xSpace = float(size[0]-2*VisOpts.xOffset)/float(nPts-1)
251 if logScale > 0:
252 v = _scaleMetric(cluster.GetMetric(), logScale)
253 else:
254 v = float(cluster.GetMetric())
255 ySpace = float(size[1]-2*VisOpts.yOffset)/v
256
257 for i in xrange(nPts):
258 pt = pts[i]
259 if logScale > 0:
260 v = _scaleMetric(pt.GetMetric(), logScale)
261 else:
262 v = float(pt.GetMetric())
263 pt._drawPos = (VisOpts.xOffset+i*xSpace,
264 size[1]-(v*ySpace+VisOpts.yOffset))
265 if not stopAtCentroids or not hasattr(pt,'_isCentroid'):
266 allNodes.remove(pt)
267
268 if not stopAtCentroids:
269 allNodes=ClusterUtils.GetNodeList(cluster)
270 else:
271 allNodes=ClusterUtils.GetNodesDownToCentroids(cluster)
272
273 while len(allNodes):
274 node = allNodes.pop(0)
275 children = node.GetChildren()
276 if len(children):
277 if logScale > 0:
278 v = _scaleMetric(node.GetMetric(), logScale)
279 else:
280 v = float(node.GetMetric())
281 yp = size[1]-(v*ySpace+VisOpts.yOffset)
282 childLocs = [x._drawPos[0] for x in children]
283 xp = sum(childLocs)/float(len(childLocs))
284 node._drawPos = (xp,yp)
285 if not stopAtCentroids or node._aboveCentroid > 0:
286 for child in children:
287 if ptColors != [] and child.GetData() is not None:
288 drawColor = ptColors[child.GetData()]
289 else:
290 drawColor = VisOpts.lineColor
291 if showNodes and hasattr(child,'_isCentroid'):
292 canvas.drawLine(child._drawPos[0],child._drawPos[1]-VisOpts.nodeRad/2,
293 child._drawPos[0],node._drawPos[1],
294 drawColor,lineWidth)
295 else:
296 canvas.drawLine(child._drawPos[0],child._drawPos[1],
297 child._drawPos[0],node._drawPos[1],
298 drawColor,lineWidth)
299 canvas.drawLine(children[0]._drawPos[0],node._drawPos[1],
300 children[-1]._drawPos[0],node._drawPos[1],
301 VisOpts.lineColor,lineWidth)
302 else:
303 for child in children:
304 drawColor = VisOpts.hideColor
305 canvas.drawLine(child._drawPos[0],child._drawPos[1],
306 child._drawPos[0],node._drawPos[1],
307 drawColor,VisOpts.hideWidth)
308 canvas.drawLine(children[0]._drawPos[0],node._drawPos[1],
309 children[-1]._drawPos[0],node._drawPos[1],
310 VisOpts.hideColor,VisOpts.hideWidth)
311
312 if showIndices and (not stopAtCentroids or node._aboveCentroid >= 0):
313 txt = str(node.GetIndex())
314 if hasattr(node,'_isCentroid'):
315 txtColor = piddle.Color(1,.2,.2)
316 else:
317 txtColor = piddle.Color(0,0,0)
318
319 canvas.drawString(txt,
320 node._drawPos[0]-canvas.stringWidth(txt)/2,
321 node._drawPos[1]+canvas.fontHeight()/4,
322 color=txtColor)
323
324 if showNodes and hasattr(node,'_isCentroid'):
325 rad = VisOpts.nodeRad
326 canvas.drawEllipse(node._drawPos[0]-rad/2,node._drawPos[1]-rad/2,
327 node._drawPos[0]+rad/2,node._drawPos[1]+rad/2,
328 piddle.transparent,
329 fillColor=VisOpts.nodeColor)
330 txt = str(node._clustID)
331 canvas.drawString(txt,
332 node._drawPos[0]-canvas.stringWidth(txt)/2,
333 node._drawPos[1]+canvas.fontHeight()/4,
334 color=piddle.Color(0,0,0))
335
336 if showIndices and not stopAtCentroids:
337 for pt in pts:
338 txt = str(pt.GetIndex())
339 canvas.drawString(str(pt.GetIndex()),
340 pt._drawPos[0]-canvas.stringWidth(txt)/2,
341 pt._drawPos[1])
342
343 -def ClusterToPDF(cluster,fileName,size=(300,300),ptColors=[],lineWidth=None,
344 showIndices=0,stopAtCentroids=0,logScale=0):
345 """ handles the work of drawing a cluster tree to an PDF file
346
347 **Arguments**
348
349 - cluster: the cluster tree to be drawn
350
351 - fileName: the name of the file to be created
352
353 - size: the size of output canvas
354
355 - ptColors: if this is specified, the _colors_ will be used to color
356 the terminal nodes of the cluster tree. (color == _pid.Color_)
357
358 - lineWidth: if specified, it will be used for the widths of the lines
359 used to draw the tree
360
361 **Notes**
362
363 - if _ptColors_ is the wrong length for the number of possible terminal
364 node types, this will throw an IndexError
365
366 - terminal node types are determined using their _GetData()_ methods
367
368 """
369 try:
370 from sping.PDF import pidPDF
371 except ImportError:
372 from piddle import piddlePDF
373 pidPDF = piddlePDF
374
375 canvas = pidPDF.PDFCanvas(size,fileName)
376 if lineWidth is None:
377 lineWidth = VisOpts.lineWidth
378 DrawClusterTree(cluster,canvas,size,ptColors=ptColors,lineWidth=lineWidth,
379 showIndices=showIndices,stopAtCentroids=stopAtCentroids,
380 logScale=logScale)
381 if fileName:
382 canvas.save()
383 return canvas
384
385 -def ClusterToSVG(cluster,fileName,size=(300,300),ptColors=[],lineWidth=None,
386 showIndices=0,stopAtCentroids=0,logScale=0):
387 """ handles the work of drawing a cluster tree to an SVG file
388
389 **Arguments**
390
391 - cluster: the cluster tree to be drawn
392
393 - fileName: the name of the file to be created
394
395 - size: the size of output canvas
396
397 - ptColors: if this is specified, the _colors_ will be used to color
398 the terminal nodes of the cluster tree. (color == _pid.Color_)
399
400 - lineWidth: if specified, it will be used for the widths of the lines
401 used to draw the tree
402
403 **Notes**
404
405 - if _ptColors_ is the wrong length for the number of possible terminal
406 node types, this will throw an IndexError
407
408 - terminal node types are determined using their _GetData()_ methods
409
410 """
411 try:
412 from sping.SVG import pidSVG
413 except ImportError:
414 from piddle.piddleSVG import piddleSVG
415 pidSVG = piddleSVG
416
417 canvas = pidSVG.SVGCanvas(size,fileName)
418
419 if lineWidth is None:
420 lineWidth = VisOpts.lineWidth
421 DrawClusterTree(cluster,canvas,size,ptColors=ptColors,lineWidth=lineWidth,
422 showIndices=showIndices,stopAtCentroids=stopAtCentroids,
423 logScale=logScale)
424 if fileName:
425 canvas.save()
426 return canvas
427
428 -def ClusterToImg(cluster,fileName,size=(300,300),ptColors=[],lineWidth=None,
429 showIndices=0,stopAtCentroids=0,logScale=0):
430 """ handles the work of drawing a cluster tree to an image file
431
432 **Arguments**
433
434 - cluster: the cluster tree to be drawn
435
436 - fileName: the name of the file to be created
437
438 - size: the size of output canvas
439
440 - ptColors: if this is specified, the _colors_ will be used to color
441 the terminal nodes of the cluster tree. (color == _pid.Color_)
442
443 - lineWidth: if specified, it will be used for the widths of the lines
444 used to draw the tree
445
446 **Notes**
447
448 - The extension on _fileName_ determines the type of image file created.
449 All formats supported by PIL can be used.
450
451 - if _ptColors_ is the wrong length for the number of possible terminal
452 node types, this will throw an IndexError
453
454 - terminal node types are determined using their _GetData()_ methods
455
456 """
457 try:
458 from sping.PIL import pidPIL
459 except ImportError:
460 from piddle import piddlePIL
461 pidPIL = piddlePIL
462 canvas = pidPIL.PILCanvas(size,fileName)
463 if lineWidth is None:
464 lineWidth = VisOpts.lineWidth
465 DrawClusterTree(cluster,canvas,size,ptColors=ptColors,lineWidth=lineWidth,
466 showIndices=showIndices,stopAtCentroids=stopAtCentroids,
467 logScale=logScale)
468 if fileName:
469 canvas.save()
470 return canvas
471