Skip to content

Commit

Permalink
Automatic chart margin determination
Browse files Browse the repository at this point in the history
  • Loading branch information
AdeelK93 committed Mar 19, 2017
1 parent f8288ae commit 0deca0e
Show file tree
Hide file tree
Showing 11 changed files with 82 additions and 56 deletions.
2 changes: 1 addition & 1 deletion DESCRIPTION
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
Package: collapsibleTree
Type: Package
Title: Interactive Collapsible Tree Diagrams using D3.js
Version: 0.1.3
Version: 0.1.4
Author: Adeel Khan [aut, cre], Michael Bostock [ctb, cph] (D3.js library)
Maintainer: Adeel Khan <AdeelK@gwu.edu>
Description: Interactive Reingold–Tilford tree diagrams created using D3.js, where every node can be expanded and collapsed by clicking on it.
Expand Down
19 changes: 14 additions & 5 deletions R/collapsibleTree.R
Original file line number Diff line number Diff line change
Expand Up @@ -75,14 +75,25 @@ collapsibleTree <- function(df, hierarchy, root = deparse(substitute(df)),
if(!(attribute %in% c(colnames(df), "leafCount"))) stop("attribute column name is incorrect")
if(sum(complete.cases(df[hierarchy])) != nrow(df)) stop("NAs in data frame")

# calculate the right and left margins in pixels
leftMargin <- nchar(root)
rightLabelVector <- as.character(unlist(df[,hierarchy[length(hierarchy)]]))
rightMargin <- max(sapply(rightLabelVector, nchar))

# create a list that contains the options
options <- list(
hierarchy = hierarchy,
input = inputId,
attribute = attribute,
linkLength = linkLength,
fontSize = fontSize,
tooltip = tooltip
tooltip = tooltip,
margin = list(
top = 20,
bottom = 20,
left = (leftMargin * fontSize/2) + 25,
right = (rightMargin * fontSize/2) + 25
)
)

# the hierarchy that will be used to create the tree
Expand Down Expand Up @@ -116,13 +127,11 @@ collapsibleTree <- function(df, hierarchy, root = deparse(substitute(df)),
} else jsonFields <- "fill"

# keep only the fill attribute in the final JSON
json <- htmlwidgets:::toJSON(
data.tree::ToListExplicit(node, unname = TRUE, keepOnly = jsonFields)
)
data <- data.tree::ToListExplicit(node, unname = TRUE, keepOnly = jsonFields)

# pass the data and options using 'x'
x <- list(
data = json,
data = data,
options = options
)

Expand Down
19 changes: 14 additions & 5 deletions R/collapsibleTreeSummary.R
Original file line number Diff line number Diff line change
Expand Up @@ -74,14 +74,25 @@ collapsibleTreeSummary <- function(df, hierarchy, root = deparse(substitute(df))
if(!(attribute %in% c(colnames(df), "leafCount"))) stop("attribute column name is incorrect")
if(sum(complete.cases(df[hierarchy])) != nrow(df)) stop("NAs in data frame")

# calculate the right and left margins in pixels
leftMargin <- nchar(root)
rightLabelVector <- as.character(unlist(df[,hierarchy[length(hierarchy)]]))
rightMargin <- max(sapply(rightLabelVector, nchar))

# create a list that contains the options
options <- list(
hierarchy = hierarchy,
input = inputId,
attribute = attribute,
linkLength = linkLength,
fontSize = fontSize,
tooltip = tooltip
tooltip = tooltip,
margin = list(
top = 20,
bottom = 20,
left = (leftMargin * fontSize/2) + 25,
right = (rightMargin * fontSize/2) + 25
)
)

# the hierarchy that will be used to create the tree
Expand Down Expand Up @@ -122,13 +133,11 @@ collapsibleTreeSummary <- function(df, hierarchy, root = deparse(substitute(df))
# keep only the JSON fields that are necessary
if(tooltip) jsonFields <- c("fill", "WeightOfNode")
else jsonFields <- "fill"
json <- htmlwidgets:::toJSON(
data.tree::ToListExplicit(node, unname = TRUE, keepOnly = jsonFields)
)
data <- data.tree::ToListExplicit(node, unname = TRUE, keepOnly = jsonFields)

# pass the data and options using 'x'
x <- list(
data = json,
data = data,
options = options
)

Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<!-- README.md is generated from README.Rmd. Please edit that file -->
collapsibleTree 0.1.3
collapsibleTree 0.1.4
---------------------

### Overview
Expand Down Expand Up @@ -87,7 +87,7 @@ shiny::runApp(system.file("examples/03shiny", package = "collapsibleTree"))
``` r
library(collapsibleTree)
date()
#> [1] "Sun Mar 19 15:01:30 2017"
#> [1] "Sun Mar 19 18:08:24 2017"

testthat::test_dir("tests/testthat")
#> Error handling: ..........
Expand Down
2 changes: 2 additions & 0 deletions docs/index.Rmd
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,8 @@ Using `dplyr` and `colorspace` again, we can create a new column in the source d

Looking at this chart, you can tell that Africa has roughly the same number of countries as Europe, and that most countries are... countries. Hovering over the node can confirm this fact.

Also note that the nodes are a little bit further apart on this example, due to individual countries not being represented. Link length and chart margins are automatically calculated based on the number of nodes and the length of the labels.

```{r plotsummary, warning=FALSE}
Geography %>%
group_by(continent, type) %>%
Expand Down
19 changes: 10 additions & 9 deletions docs/index.html

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions inst/examples/01rmd/Example01.Rmd
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,8 @@ Using `dplyr` and `colorspace` again, we can create a new column in the source d

Looking at this chart, you can tell that Africa has roughly the same number of countries as Europe, and that most countries are... countries. Hovering over the node can confirm this fact.

Also note that the nodes are a little bit further apart on this example, due to individual countries not being represented. Link length and chart margins are automatically calculated based on the number of nodes and the length of the labels.

```{r plotsummary, warning=FALSE}
Geography %>%
group_by(continent, type) %>%
Expand Down
19 changes: 10 additions & 9 deletions inst/examples/01rmd/Example01.html

Large diffs are not rendered by default.

42 changes: 22 additions & 20 deletions inst/htmlwidgets/collapsibleTree.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,32 +8,22 @@ HTMLWidgets.widget({
var i = 0,
duration = 750,
root = {},
options = {};
options = {},
treemap;

// create our tree object and bind it to the element
// Set the dimensions and margins of the diagram
var margin = {top: 20, right: 150, bottom: 20, left: 90},
width = width - margin.left - margin.right,
height = height - margin.top - margin.bottom;

// append the svg object to the body of the page
// appends a 'group' element to 'svg'
// moves the 'group' element to the top left margin
var svg = d3.select(el).append('svg')
.attr('width', width + margin.right + margin.left)
.attr('height', height + margin.top + margin.bottom)
.append('g')
.attr('transform', 'translate('
+ margin.left + ',' + margin.top + ')');
.attr('width', width)
.attr('height', height)
.append('g');

// Define the div for the tooltip
var tooltip = d3.select(el).append('div')
.attr('class', 'tooltip')
.style('opacity', 0);

// declares a tree layout and assigns the size
var treemap = d3.tree().size([height, width]);

function update(source) {

// Assigns the x and y position for the nodes
Expand Down Expand Up @@ -232,11 +222,23 @@ HTMLWidgets.widget({
root = d3.hierarchy(x.data, function(d) { return d.children; });
root.x0 = height / 2;
root.y0 = 0;

// Attach options as a property of the instance
options = x.options;

// Update the canvas with the new dimensions
svg = svg.attr('transform', 'translate('
+ options.margin.left + ',' + options.margin.top + ')')

// width and height, corrected for margins
var heightMargin = height - options.margin.top - options.margin.bottom,
widthMargin = width - options.margin.left - options.margin.right;
// declares a tree layout and assigns the size
treemap = d3.tree().size([heightMargin, widthMargin]);

// Calculate a reasonable link length, if not otherwise specified
if (!options.linkLength) {
// Calculate a reasonable link length, if not otherwise specified
options.linkLength = width / options.hierarchy.length
options.linkLength = widthMargin / options.hierarchy.length
}

// Collapse after the second level
Expand All @@ -260,9 +262,9 @@ HTMLWidgets.widget({
.attr('height', height);

// Update the treemap to fit the new canvas size
width = width - margin.left - margin.right,
height = height - margin.top - margin.bottom;
treemap = d3.tree().size([height, width]);
var heightMargin = height - options.margin.top - options.margin.bottom,
widthMargin = width - options.margin.left - options.margin.right;
treemap = d3.tree().size([heightMargin, widthMargin]);
update(root)
},
// Make the instance properties available as a property of the widget
Expand Down
2 changes: 1 addition & 1 deletion inst/htmlwidgets/collapsibleTree.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,6 @@ dependencies:
src: "htmlwidgets/lib/d3-4.5.0"
script: d3.min.js
- name: collapsibleTree
version: 0.1.3
version: 0.1.4
src: "htmlwidgets/lib"
stylesheet: collapsibleTree.css
8 changes: 4 additions & 4 deletions tests/testthat/test-root.R
Original file line number Diff line number Diff line change
Expand Up @@ -4,25 +4,25 @@ context("Root labelling")
test_that("unlabelled root works for collapsibleTree", {
wb <- collapsibleTree(warpbreaks, c("wool", "tension", "breaks"))
expect_is(wb,"htmlwidget")
expect_is(wb$x$data,"json")
expect_is(wb$x$data,"list")
expect_is(wb$x$options$hierarchy,"character")
})

test_that("unlabelled root works for collapsibleTreeSummary", {
wb <- collapsibleTreeSummary(warpbreaks, c("wool", "tension", "breaks"))
expect_is(wb,"htmlwidget")
expect_is(wb$x$data,"json")
expect_is(wb$x$data,"list")
expect_is(wb$x$options$hierarchy,"character")
})

test_that("labeled root works for collapsibleTree", {
wblabeled <- collapsibleTree(warpbreaks, c("wool", "tension", "breaks"), "a label")
expect_is(wblabeled$x$data,"json")
expect_is(wblabeled$x$data,"list")
expect_is(wblabeled$x$options$hierarchy,"character")
})

test_that("labeled root works for collapsibleTreeSummary", {
wblabeled <- collapsibleTreeSummary(warpbreaks, c("wool", "tension", "breaks"), "a label")
expect_is(wblabeled$x$data,"json")
expect_is(wblabeled$x$data,"list")
expect_is(wblabeled$x$options$hierarchy,"character")
})

0 comments on commit 0deca0e

Please sign in to comment.