// TreeMapGen.groovy // par Bertrand Goetzmann // http://www.odelia-technologies.com import edu.umd.cs.treemap.* import edu.umd.cs.treemap.test.RandomMap import groovy.swing.j2d.GraphicsRenderer import java.awt.image.BufferedImage import java.awt.Font import javax.imageio.ImageIO import groovy.xml.MarkupBuilder class TreeMapItem extends MapItem { private String description private String alt private String url private String color public TreeMapItem(String description, String alt, String url, double size, String color) { this.description = description this.alt = alt this.url = url this.size = size this.color = color } public String getCoords() { Rect r = bounds "${(int)r.x},${(int)r.y},${(int)(r.x+r.w)},${(int)(r.y+r.h)}" } } class TreemapModel implements MapModel { private Mappable[] map public TreemapModel(model) { map = new Mappable[model.items.size] int i = 0 model.items.each { map[i++] = new TreeMapItem(it.desc, it.alt, it.url, it.size, it.color) } } public Mappable[] getItems() { return map } } def model = [ width: 640, height: 480, font: [name: 'Times New Roman', size: 24], items: [ [desc: 'Groovy', alt: 'Groovy\nAn agile dynamic language for the Java Platform', url: 'http://groovy.codehaus.org/', size: 2.3, color: '#007700'], [desc: 'Grails', alt: 'Grails', url: 'http://grails.org/', size: 2, color: '#009900'], [desc: 'Treemap avec GraphicsBuilder par odelia technologies', alt: 'Treemap avec GraphicsBuilder\npar odelia technologies', url: 'http://www.odelia-technologies.com/node/155', size: 2.5, color: '#004400'], [desc: 'GraphicsBuilder, un builder Groovy pour Java 2D', alt: 'GraphicsBuilder,\nun builder Groovy pour Java 2D', url: 'http://groovy.codehaus.org/GraphicsBuilder', size: 3, color: '#006600'], [desc: 'GrailsWorks', alt: 'GrailsWorks', url: 'http://www.grailsworks.com/', size: 1, color: '#00AA00'] ] ] // Répertoire dans lequel seront générés les fichiers treemap.png et treemap.html def File folder = new File(System.properties['user.home']) println "Répertoire de génération $folder" TreemapModel treemap = new TreemapModel(model) MapLayout algorithm = new SquarifiedLayout() algorithm.layout(treemap, new Rect(0, 0, model.width-1, model.height-1)) GraphicsRenderer gr = new GraphicsRenderer() BufferedImage bimg = new BufferedImage(model.width, model.height, BufferedImage.TYPE_INT_ARGB) def image = gr.render(bimg) { antialias( 'on' ) rect( x: 0, y: 0, width: model.width, height: model.height, fill: 'white', borderColor: 'gray') // La police à utiliser def font = new Font(model.font.name, Font.PLAIN, model.font.size) def g = bimg.createGraphics() g.setFont(font) def fm = g.getFontMetrics() // Permet de positionner correctement une ligne de texte def ht = fm.height - fm.descent def marge = 10 Rect r treemap.items.each { r = it.getBounds() // Dessiner le rectangle correspondant au MapItem rect(x: r.x, y: r.y, width: r.w, height: r.h, fill: color(it.color), borderColor: 'gray') def desc= "${it.description}" // Dessinons le texte desc def dx = r.x + marge def dy = r.y + marge def affiche = false // La closure permettant de dessiner le texte t horizontalement // grâce au noeud textStroke drawText = { t, x1, y1, x2 -> xpath(borderColor: 'white') { textStroke(text: t, font : font) xmoveTo(x: x1, y: y1) xhline(x: x2) } } def bounds // Dessiner chaque mot de la description si possible desc.split().find { bounds = fm.getStringBounds(it, g) // Le mot dépasse t-il du bord droit ? if (dx + bounds.width >= r.x + r.w) { // Passer à la ligne ! dx = r.x + marge dy += fm.height } // Y a t-il de la place pour dessiner le mot ? if (dx + bounds.width < r.x + r.w && dy + fm.height < r.y + r.h) { drawText(it + ' ', dx, dy + ht, dx + r.w) affiche = true dx += fm.getStringBounds(it + ' ', g).width return false } else { bounds = fm.getStringBounds('...', g) if (dx + bounds.width < r.x + r.w && dy + fm.height < r.y + r.h) affiche ? drawText('...', dx, dy + ht, dx + r.w) : drawText('...', r.x + marge, r.y + ht + marge, r.x + r.w) return true } } } } // Créer le fichier treemap.png ImageIO.write(image, "png", new File(folder, 'treemap.png')) // Créons maintenant le fichier treemap.html def writer = new FileWriter(new File(folder, 'treemap.html')) def builder = new MarkupBuilder(writer) builder.html { body { img(src: 'treemap.png', usemap: '#treemap', border: '0') map(name: 'treemap') { treemap.items.each { area( shape: 'rect', href: "${it.url}", alt: "${it.alt}", title: "${it.description}", coords: "${it.coords}" ) } } } } writer.close()