################################################################### ## ## ## FUNCTION FOR FORMING ALL OF THE GROBS ## ## ## ################################################################### ### (1) We first form the polygon grobs. ### (2) Then we need to do some checks before forming the lines grobs. ### After running the label placement algorithm, it is possible that ### some of the lines between polygon centroids and label positions ### overlap. To fix this, we simply order both sets of y coordinates ### to match pairs of points without any line overlap. ### (3) Then we form the lines and labels grobs. Each line requires ### multiple lines grobs due to the difference in start and end ### y coordinates. ### A list of gLists is returned, each containing a group of grobs ### ready to be drawn. ### A description of the grobs naming scheme: ### The name of each grobs always includes the name of the top level ### group that it is related to. The subLevel polygons are named as ### such: the grob representing the the i-th subgroup of the group ### named "groupName" is given the name subPgon.groupName.i makeGrobs = function(polyVars, cpiData, priceChanges, pgonCoords, posLabs, fillCols, label.margin = 0.05) { namesData = niceNames(cpiData$groupNames) n.groups = polyVars$n.groups top.polygons = gList() sub.polygons = gList() subsub.polygons = gList() if (!is.null(polyVars$n.subsub)) { ids = unlist(sapply(polyVars$subsubs.per.group, function(ni) 1:ni), use.names = FALSE) for (i in 1:polyVars$total.subsubPgons) { g = polyVars$group.subsub[i] subsub.name = paste("subsubPgon", namesData[g], ids[i], sep = ".") subsub.polygons[[subsub.name]] = makePolygon(pgonCoords$subsub[[i]], name = subsub.name, gp = gpar(col = "#cacaca", lwd = 0.5, fill = fillCols[i])) } sub_fill = FALSE } else { sub_fill = TRUE } ids = unlist(sapply(polyVars$n.sub, function(ni) 1:ni), use.names = FALSE) for (i in 1:polyVars$total.subPgons) { g = polyVars$group.sub[i] sub.name = paste("subPgon", namesData[g], ids[i], sep = ".") sub_gpar = gpar(col = "#ababab", lwd = 1.5) if (sub_fill) sub_gpar$fill = fillCols[i] sub.polygons[[sub.name]] = makePolygon(pgonCoords$sub[[i]], name = sub.name, gp = sub_gpar) } for (i in 1:n.groups) { topName = paste("topPgon", namesData[i], sep = ".") top.polygons[[topName]] = makePolygon(pgonCoords$top[[i]], name = topName, gp = gpar(lwd = 4, col = "#727272")) } ##________ LINES AND LABELS ________## ### the lines start at the original y.npc (centroids) lines.y.start = posLabs$centroidYnpc ### this is where the lines end (they end in the side panels) ### note we will have to form bends in the lines lines.y.end = posLabs$centroidYnpc + posLabs$labBot - posLabs$origLabBot ## more reordering, to fix overlapping lines. d = data.frame(y0 = lines.y.start, y1 = lines.y.end, panel = posLabs$panel, x.main = posLabs$centroidX, y.main = posLabs$centroidY, labstr = posLabs$formatted.strings, group.id = 1:n.groups) d2 = data.frame() for (s in c("left", "right")) { d.side = subset(d, panel == s) d.ord = order(d.side$y0) d.side$labstr = d.side$labstr[d.ord] d.side$x.main = d.side$x.main[d.ord] d.side$y.main = d.side$y.main[d.ord] d.side$group.id = d.side$group.id[d.ord] d.side$y0 = sort(d.side$y0) d.side$y1 = sort(d.side$y1) d2 = rbind(d2, d.side) } d2$panel = as.character(d2$panel) bend.width = 0.1 # npc line.col = "#454545" lines = gList() labels = gList() ## we start in the central viewport and draw towards the sides ## here we also create the labels for our image for (i in 1:n.groups) { i.lab = d2$group.id[i] x.bend = switch(d2$panel[i], "left" = 0.88, "right" = 0.12) x.end = switch(d2$panel[i], "left" = label.margin, "right" = 1 - label.margin) line1 = paste("line1", namesData[i.lab], sep = ".") line2 = paste("line2", namesData[i.lab], sep = ".") moveToStartBg = paste("lineStart", namesData[i.lab], "background", sep = ".") lines[[moveToStartBg]] = moveToGrob(x = d2$x.main[i], y = d2$y.main[i], default.units = "native", vp = vpPath("parent", "main"), name = moveToStartBg) line1Background = paste(line1, "background", sep = ".") lines[[line1Background]] = lineToGrob(x = x.bend, y = d2$y1[i], default.units = "npc", gp = gpar(col = "white", lwd = 5, alpha = 0.63), vp = vpPath("parent", d2$panel[i]), name = line1Background) line2Background = paste(line2, "background", sep = ".") lines[[line2Background]] = lineToGrob(x = x.end, y = d2$y1[i], default.units = "npc", gp = gpar(col = "white", lwd = 5, alpha = 0.63), vp = vpPath("parent", d2$panel[i]), name = line2Background) moveToStart = paste("moveToStart", namesData[i.lab], sep = ".") lines[[moveToStart]] = moveToGrob(x = d2$x.main[i], y = d2$y.main[i], default.units = "native", vp = vpPath("parent", "main"), name = moveToStart) lines[[line1]] = lineToGrob(x = x.bend, y = d2$y1[i], default.units = "npc", gp = gpar(col = line.col), vp = vpPath("parent", d2$panel[i]), name = line1) lines[[line2]] = lineToGrob(x = x.end, y = d2$y1[i], default.units = "npc", gp = gpar(col = line.col), vp = vpPath("parent", d2$panel[i]), name = line2) labelName = paste("label", namesData[i.lab], sep = ".") labels[[labelName]] = makeLabel(d2$labstr[i], d2$panel[i], labelName, unit(d2$y1[i], "npc") + unit(2, "mm")) weightName = paste("weightLabel", namesData[i.lab], sep = ".") weightFormat = ifelse(cpiData$cpi.groups[i.lab] < 0.05, "%.2f%%", "%.1f%%") labels[[weightName]] = makeLabel(paste("Weight:", gettextf(weightFormat, cpiData$cpi.groups[i.lab])), d2$panel[i], weightName, unit(d2$y1[i], "npc") - unit(5, "mm"), type = "weight") changeName = paste("changeLabel", namesData[i.lab], sep = ".") tmp = namesData[i.lab] labels[[changeName]] = makeLabel(paste("Change:", priceChanges$groupChanges[tmp]), d2$panel[i], changeName, unit(d2$y1[i], "npc") - unit(10, "mm"), type = "change") dot = paste("dot", namesData[i.lab], sep = ".") lines[[dot]] = pointsGrob(x = d2$x.main[i], y = d2$y.main[i], pch = 19, size = unit(2, "mm"), vp = vpPath("parent", "main"), name = dot) } list(topPgons = top.polygons, subPgons = sub.polygons, subsubPgons = subsub.polygons, labels = labels, lines = lines) } ################################################################### ## ## ## FUNCTIONS FOR FORMING POLYGONS AND LABELS ## ## ## ################################################################### makePolygon <- function(p, gp, name) { polygonGrob(p@pts[[1]]$x, p@pts[[1]]$y, default = "native", gp = gp, name = name, vp = vpPath("parent", "main")) } makeLabel = function(label, panel, grobName, yUnit, label.margin = 0.05, temp = FALSE, type = "title") { x.mar = ifelse(panel == "left", label.margin, 1 - label.margin) hjust = ifelse(panel == "left", 0, 1) labels.gpar = switch(type, "title" = gpar(cex = 0.9, col = "black", fontface = "bold"), "weight" = gpar(cex = 0.8, col = "#808080"), "change" = gpar(cex = 0.8, col = "black")) tg = textGrob(label, hjust = hjust, vjust = 0, x = unit(x.mar, "npc"), y = yUnit, name = grobName, gp = labels.gpar) if (!temp) tg$vp = vpPath("parent", panel) tg } ################################################################### ## ## ## FUNCTION FOR FORMING THE INFO GROBS ## ## ## ################################################################### ### All other grobs made here makeInfoGrobs = function(priceChanges, groupname = NULL) { infoGrobs = gList() comparisonStr = paste("Comparing", priceChanges$comparison, "prices to", priceChanges$baseline, "prices") infoGrobs$comparisonInfo = textGrob(comparisonStr, y = unit(1, "npc") - unit(2, "mm"), just = c("centre", "bottom"), name = "comparisonText", gp = gpar(fontface = "bold", cex = 1.3), vp = vpPath("parent", "top")) scaleY = 0.4 if (!is.null(groupname)) { subHeading = paste("Group:", groupname); infoGrobs$subHeading = textGrob(subHeading, y = unit(1, "npc") - unit(4, "mm"), just = c("centre", "top"), name = "subHeading", gp = gpar(cex = 1.2), vp = vpPath("parent", "top")) scaleY = 0.3 } numCols = length(priceChanges$cols) seekViewport("top") scaleLength = unit(100, "mm") scaleHeight = unit(3, "mm") scaleEnd = unit(1, "npc") - unit(10, "mm") scaleStart = scaleEnd - scaleLength rectWidth = convertWidth(scaleLength, "mm", TRUE) / numCols for (i in 1:numCols) { rectName = paste("scaleRect", i, sep = "") x0 = scaleStart + unit((i-1) * rectWidth, "mm") infoGrobs[[rectName]] = rectGrob(x = x0, y = unit(scaleY, "npc"), width = unit(rectWidth, "mm"), height = scaleHeight, just = c("left", "bottom"), name = rectName, gp = gpar(fill = priceChanges$cols[i], col = "black"), vp = vpPath("parent", "top")) } for (i in 1:(numCols-1)) { labName = paste("scaleLab", i, sep = "") labText = priceChanges$breakpoints[i + 1] if (as.numeric(labText) > 0) labText = paste("+", labText, sep = "") infoGrobs[[labName]] = textGrob(labText, x = scaleStart + unit(i * rectWidth, "mm"), y = unit(scaleY, "npc") - unit(1, "mm"), just = c("centre", "top"), name = labName, gp = gpar(cex = 0.8), vp = vpPath("parent", "top")) } infoGrobs$legendLab = textGrob("Price change (percent)", x = scaleStart + unit(rectWidth * numCols/2, "mm"), y = unit(scaleY, "npc") + scaleHeight + unit(3, "mm"), just = c("centre", "bottom"), name = "legendLabel", gp = gpar(cex = 0.9), vp = vpPath("parent", "top")) ## back link for sub-kaleidoscopes if (!is.null(groupname)) { infoGrobs$backLink = textGrob("Back", x = unit(0.5, "npc"), y = unit(0.25, "npc"), name = "backLink", gp = gpar(col = "blue"), vp = vpPath("parent", "bottom")) } infoGrobs }