import java.text.DateFormatSymbols; import java.text.DecimalFormat; import java.util.ArrayList; import java.util.Stack; import processing.core.*; import treemap.*; public class SuperMap extends PApplet { // public static void main(String args[]) // { // PApplet.main(new String[] { "--bgcolor=#ffffff", "SuperMap" }); // } class TreeMapNode extends SimpleMapItem implements MapModel { String name; int size; int clr; ArrayList children; TreeMapNode(String name, int size, int clr) { this.name = name; this.size = size; this.clr = clr; this.children = new ArrayList(); } void addChild(TreeMapNode child) { children.add(child); } void addChildAndSum(TreeMapNode child) { children.add(child); size += child.size; } TreeMapNode getChild(int i) { return children.get(i); } public Mappable[] getItems() { return (Mappable[]) children.toArray(new Mappable[children.size()]); } public double getSize() { return size; } public void draw() { fill(clr); stroke(0); strokeWeight(0.25f); rect(x, y, w, h); strokeWeight(1f); } boolean hover() { if (mouseX > x && mouseX < x + w && mouseY > y && mouseY < y + h) return true; return false; } } public class Book { public String Title; public int count; } public class Font { PFont font; float size; Font(PFont font, float size) { this.font = font; this.size = size; } } String deweyClasses[] = { "Computer science, information and general works", "Philosophy and psychology", "Religion", "Social sciences", "Language", "Science", "Technology and applied Science", "Arts and recreation", "Literature", "History, geography, and biography", "Non-Dewey" }; String artsClasses[] = { "Arts", "Civic & landscape art", "Architecture", "Plastic arts; Sculpture", "Drawing & decor. arts", "Painting & paintings", "Graphic arts; Printmaking & prints", "Photography & photographs", "Music", "Recreational & performing arts" }; String drawingClasses[] = { "Drawing & decorative arts", "Drawing & drawings", "Perspective (graphical)", "Drawing & drawings by subject", "Not assigned or no longer used", "Decorative arts", "Textile arts", "Interior decoration", "Glass", "Furniture & accessories" }; Font fontScale; Font fontCount; Font fontLabels; Font fontPath; Font fontTitle; Font fontMonth; Font fontYear; DecimalFormat decimal = new DecimalFormat(); String[] monthNames = (new DateFormatSymbols()).getMonths(); int windowWidth = 1024; int windowHeight = 768; int mapWidth = 768; int mapHeight = 768; float unitSide; float scale = 1; int yearMax = 2011; int yearMin = 2005; int monthCount = (yearMax - yearMin) * 12; String filenameDewey = "dewey-count.txt"; String filenameArts = "arts-count.txt"; String filenameDrawing = "drawing-count.txt"; String filenameTitlesSuper = "titles-super.txt"; String filenameTitlesOther = "titles-other.txt"; String folderListBook = "list-book/"; String suffixListBook = "-list-book.txt"; String titlesSuper[]; String namesSuper[]; int colorsSuper[]; int[][] valuesSuper; ArrayList[][] booksSuper; String titlesOther[]; String namesOther[]; int colorsOther[]; int[][] valuesOther; ArrayList[][] booksOther; int deweyCount[][] = new int[monthCount][11]; int artsCount[][] = new int[monthCount][10]; int drawingCount[][] = new int[monthCount][10]; TreeMapNode treeRoot; TreeMapNode selectedRoot; Stack indexPath = new Stack(); int selectedMonth = 0; public void setup() { size(windowWidth, windowHeight); smooth(); strokeWeight(0.5f); stroke(0); cursor(CROSS); colorMode(HSB,360,100,100); PFont fontDVSans12 = loadFont("fonts/DejaVuSans-12.vlw"); PFont fontDVSansBold12 = loadFont("fonts/DejaVuSans-Bold-12.vlw"); PFont fontDVSansBold16 = loadFont("fonts/DejaVuSans-Bold-16.vlw"); PFont fontArialBoldMT40 = loadFont("fonts/Arial-BoldMT-40.vlw"); fontLabels = new Font(fontDVSans12, 12); fontCount = new Font(fontDVSansBold12, 12); fontPath = new Font(fontDVSansBold16, 16); fontTitle = new Font(fontDVSansBold16, 16); fontMonth = new Font(fontDVSansBold16, 16); fontYear = new Font(fontArialBoldMT40, 40); loadTitlesSuper(); loadTitlesOther(); loadDataSuper(); loadDataOther(); loadDewey(); loadSubclass(artsCount, filenameArts); loadSubclass(drawingCount, filenameDrawing); buildTree(); indexPath.add(selectedMonth); selectedRoot = treeRoot.getChild(selectedMonth); buildMap(); } public void draw() { background(0xffffffff); drawMap(); drawTime(); drawPath(); drawScale(); } void drawScale() { fill(0xff000000); stroke(0); strokeWeight(3); noFill(); rect( mapWidth + (windowWidth - mapWidth)/2 - unitSide/2, mapHeight - 50 - unitSide, unitSide, unitSide); textAlign(CENTER,CENTER); textFont(fontScale.font); fill(0xff000000); String label = str(scale); if (scale >= 1) label = str((int)scale); float fontSize = unitSide; textFont(fontScale.font, fontSize); while (textWidth(label) > unitSide - 10) { fontSize *= 0.75f; textFont(fontScale.font, fontSize); } text(label, mapWidth + (windowWidth - mapWidth)/2, mapHeight - 50 - unitSide/2 - fontSize*0.10f); textFont(fontCount.font); text("CHECKOUTS: " + decimal.format(selectedRoot.size), mapWidth + (windowWidth - mapWidth)/2, mapHeight - 30); strokeWeight(1); } void drawMap() { Mappable[] RootItems = selectedRoot.getItems(); for (int i = 0; i < RootItems.length; i++) { TreeMapNode node = (TreeMapNode)RootItems[i]; Mappable[] items = node.getItems(); if (items.length == 0) { node.draw(); } else { for (int j = 0; j < items.length; j++) { ((TreeMapNode) items[j]).draw(); } } drawLabel(node); } for (int i = 0; i < RootItems.length; i++) { TreeMapNode node = (TreeMapNode)RootItems[i]; if (node.hover()) { Mappable[] items = node.getItems(); if (items.length == 0) { stroke(0); noFill(); rect(node.x, node.y, node.w, node.h); drawPopup(node); break; } for (int j = 0; j < items.length; j++) { TreeMapNode item = (TreeMapNode)items[j]; if (item.hover()) { noFill(); stroke(0); rect(item.x, item.y, item.w, item.h); stroke(0xffffffff); rect(node.x, node.y, node.w, node.h); drawPopup(item); break; } } break; } } } void drawLabel(TreeMapNode node) { String label = node.name; textFont(fontTitle.font); while (textWidth(label) > node.w) { if (label.length() < 10) break; label = label.substring(0, label.length()-4).trim() + "..."; } if (textWidth(label) < node.w && node.h > 20 && node.getSize() > selectedRoot.size * 0.05f) { textFont(fontTitle.font); fill(0xffffffff); textAlign(CENTER, CENTER); text(label, node.x + node.w/2, node.y + node.h/2); } } void drawPopup(TreeMapNode node) { if (mouseX > node.x && mouseX < node.x + node.w && mouseY > node.y && mouseY < node.y + node.h) { String label1 = node.name; String label2 = "CHECKOUTS: " + (int)node.size; textFont(fontTitle.font); while( textWidth(label1) > mapWidth / 2 - 20) label1 = label1.substring(0, label1.length()-4).trim() + "..."; float l1w = textWidth(label1); textFont(fontLabels.font); float l2w = textWidth(label2); float tw = max(l1w, l2w); float th = fontTitle.size * 1.5f + fontLabels.size; float yy = mouseY; float xx = mouseX; if (mouseX > mapWidth / 2) xx = mouseX - tw - 20; if (mouseY > mapHeight / 2) yy = mouseY - th - 15; stroke(0); fill(0x66000000); rect(xx, yy, tw + 20, fontTitle.size * 1.5f + fontLabels.size + 15); textAlign(LEFT, CENTER); fill(0xffffffff); textFont(fontTitle.font); text(label1, xx + 10, yy + fontTitle.size); textFont(fontLabels.font); text(label2, xx + 10, yy + fontTitle.size * 1.5f + fontLabels.size); } } void drawPath() { stroke(0); noFill(); textAlign(CENTER); textFont(fontPath.font); float x = mapWidth + (windowWidth - mapWidth) / 2; TreeMapNode currentNode = treeRoot.getChild(indexPath.get(0)); for (int i = 1; i < indexPath.size(); i++) { currentNode = currentNode.getChild(indexPath.get(i)); fill(0xff000000 + 0xff222222 * (indexPath.size()-1-i) ); String text = currentNode.name.toUpperCase(); text(text, x, 40 + 70 * i + 45); float tw = textWidth(text); beginShape(); vertex(x - 10, 40 + 70 * i + 5); vertex(x + 10, 40 + 70 * i); endShape(); } } void drawTime() { int y = yearMin + selectedMonth / 12; int m = selectedMonth % 12; fill(0); textAlign(CENTER); textFont(fontMonth.font); text(monthNames[m].toUpperCase(), mapWidth + (windowWidth - mapWidth)/2, 40); textFont(fontYear.font); text(Integer.toString(y), mapWidth + (windowWidth - mapWidth)/2, 80); } TreeMapNode buildComicsTree(int month) { TreeMapNode comics = new TreeMapNode("Comics with movies", 0, color(210,100,90)); TreeMapNode comicsSuper = new TreeMapNode("Superhero comics", 0, color(190,100,100)); for (int i = 0; i < titlesSuper.length; i++) { TreeMapNode nodeT = new TreeMapNode(namesSuper[i], 0, color(colorsSuper[i], 80, 90)); for (int j = 0; j < booksSuper[i][month].size(); j++) { int count = booksSuper[i][month].get(j).count; String name = booksSuper[i][month].get(j).Title; int trend = getTrend(booksSuper, i, month, name, count); int clr = color( colorsSuper[i], 80, 80 + trend * 10); nodeT.addChildAndSum(new TreeMapNode(name, count, clr)); } comicsSuper.addChildAndSum(nodeT); } TreeMapNode comicsOther = new TreeMapNode("Other comics", 0, color(170,100,100)); for (int i = 0; i < titlesOther.length; i++) { TreeMapNode nodeT = new TreeMapNode(namesOther[i], 0, color(colorsOther[i], 80, 90)); for (int j = 0; j < booksOther[i][month].size(); j++) { int count = booksOther[i][month].get(j).count; String name = booksOther[i][month].get(j).Title; int trend = getTrend(booksOther, i, month, name, count); int clr = color( colorsOther[i], 80, 80 + trend * 10); nodeT.addChildAndSum(new TreeMapNode(name, count, clr)); } comicsOther.addChildAndSum(nodeT); } comics.addChildAndSum(comicsSuper); comics.addChildAndSum(comicsOther); return comics; } public void buildTree() { treeRoot = new TreeMapNode("root", 0, color(0,100,100)); for (int year = yearMin; year < yearMax; year++) { for (int month = 0; month < 12; month++) { int m = (year - yearMin) * 12 + month; String nameM = monthNames[month] + " " + year; int countM = 0; int clrM = color(month * 360 / 23.0f, 80, 90); TreeMapNode nodeM = new TreeMapNode(nameM, countM, clrM); // Dewey for (int i = 0; i < 11; i++) { String nameD = deweyClasses[i]; int countD = deweyCount[m][i]; int clrD = color(i * 360 / 14.0f, 60, 60); TreeMapNode nodeD = new TreeMapNode(nameD, countD, clrD); // Arts if (i == 7) { for (int x = 0; x < 10; x++) { String name7x0 = artsClasses[x]; int count7x0 = artsCount[m][x]; int clr7x0 = color(i*360/14.0f + x*(360/14.0f)/10.0f, 75, 75); TreeMapNode node7x0 = new TreeMapNode(name7x0, count7x0, clr7x0); // Drawing if (x == 4) { for (int y = 0; y < 10; y++) { String name74y = drawingClasses[y]; int count74x = drawingCount[m][y]; int clr74y = color((180 + y*360/10.0f)%360, 75, 75); TreeMapNode node74y = new TreeMapNode(name74y, count74x, clr74y); // Drawing & drawings if (y == 1) { // comics TreeMapNode comics = buildComicsTree(m); node74y.addChild(comics); // other String name = "Other"; int clr = color(220, 90, 90); TreeMapNode other = new TreeMapNode(name, count74x - comics.size, clr); node74y.addChild(other); } node7x0.addChild(node74y); } } nodeD.addChild(node7x0); } } nodeM.addChildAndSum(nodeD); } treeRoot.addChildAndSum(nodeM); } } } public void buildMap() { // MapLayout nodeLayout = new SquarifiedLayout(); MapLayout nodeLayout = new PivotBySplitSize(); // MapLayout leafLayout = new SquarifiedLayout(); MapLayout leafLayout = new PivotBySplitSize(); nodeLayout.layout(selectedRoot, 0, 0, mapWidth, mapHeight); Mappable[] RootItems = selectedRoot.getItems(); for (int i = 0; i < RootItems.length; i++) { TreeMapNode item = (TreeMapNode) RootItems[i]; if (item.children.size() > 0) { leafLayout.layout(item, item.x, item.y, item.w, item.h); } } int area = mapWidth * mapHeight; float unitArea = area / (float)selectedRoot.size; unitSide = sqrt(unitArea); scale = 1; while (unitSide < 70) { unitArea *= 10; unitSide = sqrt(unitArea); scale *= 10; } while (unitSide > (windowWidth - mapWidth) * 0.5f) { unitArea /= 10; unitSide = sqrt(unitArea); scale /= 10; } float fontSize = unitSide * 1f; String fontName = "data/fonts/Arial_Bold.ttf"; fontScale = new Font( createFont(fontName, fontSize), fontSize ); } int getTrend(ArrayList booklist[][], int title, int month, String name, int count) { if (month == 0) return 2; for(int i = 0; i < booklist[title][month-1].size(); i++) { if (name.equals(booklist[title][month-1].get(i).Title)) { int diff = count - booklist[title][month-1].get(i).count; if (diff > 0) return 1; else if (diff < 0) return -1; else return 0; } } return 2; } void loadTitlesSuper() { String lines[] = loadStrings(filenameTitlesSuper); titlesSuper = new String[lines.length]; namesSuper = new String[lines.length]; colorsSuper = new int[lines.length]; for (int i = 0; i < lines.length; i++) { String[] line = lines[i].split(", "); titlesSuper[i] = line[0]; namesSuper[i] = line[1]; colorsSuper[i] = (int)Integer.parseInt(line[2]); } } void loadTitlesOther() { String lines[] = loadStrings(filenameTitlesOther); titlesOther = new String[lines.length]; namesOther = new String[lines.length]; colorsOther = new int[lines.length]; for (int i = 0; i < lines.length; i++) { String[] line = lines[i].split(", "); titlesOther[i] = line[0]; namesOther[i] = line[1]; colorsOther[i] = (int)Integer.parseInt(line[2]); } } void loadDataSuper() { valuesSuper = new int[titlesSuper.length][monthCount]; booksSuper = (ArrayList[][]) new ArrayList[titlesSuper.length][monthCount]; for (int j = 0; j < titlesSuper.length; j++) { String filename = folderListBook + titlesSuper[j] + suffixListBook; String lines[] = loadStrings(filename); booksSuper[j] = (ArrayList[]) new ArrayList[monthCount]; for (int i = 0, k = -1; i < lines.length; i++) { if (lines[i].startsWith("###")) { k++; valuesSuper[j][k] = 0; booksSuper[j][k] = new ArrayList(); continue; } String[] line = lines[i].split(" , "); Book book = new Book(); book.Title = line[0]; book.count = Integer.valueOf(line[1]); booksSuper[j][k].add(book); valuesSuper[j][k] += book.count; } } } void loadDataOther() { valuesOther = new int[titlesOther.length][monthCount]; booksOther = (ArrayList[][]) new ArrayList[titlesOther.length][monthCount]; for (int j = 0; j < titlesOther.length; j++) { String filename = folderListBook + titlesOther[j] + suffixListBook; String lines[] = loadStrings(filename); booksOther[j] = (ArrayList[]) new ArrayList[monthCount]; for (int i = 0, k = -1; i < lines.length; i++) { if (lines[i].startsWith("###")) { k++; valuesOther[j][k] = 0; booksOther[j][k] = new ArrayList(); continue; } String[] line = lines[i].split(" , "); Book book = new Book(); book.Title = line[0]; book.count = Integer.valueOf(line[1]); booksOther[j][k].add(book); valuesOther[j][k] += book.count; } } } void loadSubclass(int[][] count, String filename) { String lines[] = loadStrings(filename); for (int i = 0, m = 0; i < lines.length; i += 11, m++) { for (int j = 0; j < 10; j++) { String columns[] = lines[i+1+j].split(" : "); count[m][j] = Integer.parseInt(columns[1]); } } } void loadDewey() { String lines[] = loadStrings(filenameDewey); for (int i = 0, m = 0; i < lines.length; i += 12, m++) { for (int j = 0; j < 11; j++) { String columns[] = lines[i+1+j].split(" : "); deweyCount[m][j] = Integer.parseInt(columns[1]); } } } public void keyPressed() { if (key == CODED) { switch(keyCode) { case RIGHT: selectedMonth++; break; case LEFT: selectedMonth--; break; case UP: selectedMonth += 12; break; case DOWN: selectedMonth-=12; break; } if (selectedMonth < 0) selectedMonth = 0; else if (selectedMonth >= monthCount) selectedMonth = monthCount - 1; indexPath.set(0, selectedMonth); selectedRoot = treeRoot.getChild(selectedMonth); for (int i = 1; i < indexPath.size(); i++) { TreeMapNode node = selectedRoot.getChild(indexPath.get(i)); if (node.size > 0) { selectedRoot = node; } else { int pathsize = indexPath.size(); for (int j = i; j < pathsize; j++) { indexPath.pop(); } break; } } buildMap(); } } public void mousePressed() { Mappable[] items = selectedRoot.getItems(); for (int i = 0; i < items.length; i++) { TreeMapNode node = (TreeMapNode)items[i]; float x = node.x; float y = node.y; float w = node.w; float h = node.h; if (mouseX > x && mouseX < x + w && mouseY > y && mouseY < y + h) { if (mouseButton == LEFT) { if (node.children.size() > 0) { indexPath.add(i); selectedRoot = node; buildMap(); } } else if (mouseButton == RIGHT) { if (indexPath.size() > 1) { indexPath.pop(); selectedRoot = treeRoot.getChild(indexPath.get(0)); for (int j = 1; j < indexPath.size(); j++) selectedRoot = selectedRoot.getChild(indexPath.get(j)); buildMap(); } } break; } } } }