How to integrate a data-driven d3JS chart with Shiny?

Yesterday I asked how to bring a d3js javacript file with offline data to Shiny in order to draw a power network graph. Now I'm looking for the next step: server.R will read the JSON data file for display on the chart. I tried to adapt this example , which uses messageHandlers to transfer data to d3JS. This is beyond my experience, so I'm looking for some help. I am pretty sure this is in messageHandler where everything goes wrong.

I will gladly publish a full working example, because it will lead me to a new level of understanding of the integration of R, Shiny and d3JS. PS: Yes, I looked at networkD3 and other tools. My d3JS diagrams are much more complicated than a simple example. :) The following steps also include creating a graph that responds to input choices in Shiny, but I need this problem first of all. Thank you very much! Tim

ui.R - click the button, get the schedule!

shinyUI(fluidPage(
  titlePanel("Shiny Binding to d3JS"),
  sidebarLayout(
    sidebarPanel(
      tags$head(tags$link(rel = "stylesheet", type = "text/css", href = "twoNodes.css")),
      actionButton("var_run",label="Create Graph!")
      ),
    mainPanel(
      h3("D3JS FN OUTPUT:"),
      # load D3JS library
      tags$script(src="d3.min.js"),
      # load javascript
      tags$script(src="twoNodes.js"),
      # create div
      tags$div(id="div_graph")
    )
  )
)) 

server.R - Currently read in two nodes and their link. IRL will request a data warehouse.

library(shiny)
library(rjson)
# Read JSON from the file
json_data <- fromJSON(file="twoNodes.JSON")

shinyServer(
  function(input, output, session) {
    # exception handler for when action button is clicked
    # session$sendCustomMessage is where things start to fall apart
    observe({
      if (input$var_run == 0){
        return()
      } 
      session$sendCustomMessage(type="jsondata",json_data)
      })
  }
)

twoNodes.JSON - data

{
        "nodes":[
          {"id":0,"name":"Observations","group":"1"},
          {"id":1,"name":"DataSet","group":"2"}
          ],
        "edges":[
          {"source":0,"target":1,"value":""}
          ]
    }

twoNodes.css - style sheet

#nodegroup1{
    fill:#000000;
    fon-family: Serif, Georgia;
    font-size: 14px;   
     font-weight: bold;
  }              
  .nodetext{
     font-size:8;
     color: red;
  }
  .edgelabel{
    font-size:12px;
    fill:darkblue;
  }
  .edges{
    stroke: #ccc;
    stroke-width: 2;
   }

twoNodes.js is the d3JS magic I'm trying to use

Shiny.addCustomMessageHandler("jsondata",
  function(message){
    var dataset = [message];

   d3.select("#tempID").remove()

  // lines from original d3JS follow
  //Width and height for SVG area
  var w = 300;
  var h = 200;
  // changed from body to #div_graph. Added tempID
  var svg = d3.select("#div_graph").append("svg")
              .attr("id","tempID")
              .attr("width", w)
              .attr("height", h);

  svg.append("text")
     .text("Two Nodes in a Force Network")
     .attr("x",10)
     .attr("y",15);

// Data source   - Now comes in with message handler
// d3.json("/d3/CubeStructure/twoNodes.JSON", function(dataset) {

  var force = d3.layout.force()
                .nodes(dataset.nodes)
                .links(dataset.edges)
                .gravity(.05)
                .charge(-180)
                .linkDistance(100)
                .size([w, h])
                .start();
  var drag = force.drag()
                  .on("dragstart", dragstart);

  var edges = svg.selectAll("line")
                 .data(dataset.edges)
                 .enter()
                 .append("line")
                 .attr("id",function(d,i){return 'edge'+i})
                 .attr("class", "edges")
                 .attr("marker-end", "url(#end)");  

  var nodes = svg.selectAll("g.node")
                 .data(dataset.nodes)
                 .enter()
                 .append("g")
                 .attr("class", "node")
                 .on("dblclick", dblclick)
                 .call(drag);
  nodes.append("circle")
       .attr("r", 10)
       .style("stroke", "black") 
       // Mousover Node - highlight node by fading the node colour during mouseover
       .on('mouseover', function(d){
            var nodeSelection = d3.select(this).style({opacity:'0.5'});
       })
       //Mouseout Node  - bring node back to full colour   
       .on('mouseout', function(d){
            var nodeSelection= d3.select(this).style({opacity:'1.0',}) 
       })

 // Node label
  nodes.append("text")
       .attr("class", "nodetext")
       .attr("dx", 12)
       .attr("dy", ".35em")
       .attr("id", function(d,i){return 'nodegroup1'}) // all get the same style
       .text(function(d) { return d.name });            // Just the name
  // Paths along which to apply the edge label
  var edgepaths = svg.selectAll(".edgepath")
                     .data(dataset.edges)
                     .enter()
                     .append('path')
                     .attr({'d': function(d) {return 'M '+d.source.x+' '+d.source.y+' L '+ d.target.x +' '+d.target.y},
                            'class':'edgepath',
                            'fill-opacity':0,
                            'stroke-opacity':0,
                            'fill':'blue',
                            'stroke':'red',
                            'id':function(d,i) {return 'edgepath'+i}})
                     .style("pointer-events", "none");
  // dx : the starting distance of the label from the source node
  var edgelabels = svg.selectAll(".edgelabel")
                      .data(dataset.edges)
                      .enter()
                      .append('text')
                      .style("pointer-events", "none")
                     .attr({'class':'edgelabel',
                             'id':function(d,i){return 'edgelabel'+i},
                             'dx':40,
                             'dy':0
                            }) ;
    force.on("tick", function() {
             edges.attr("x1", function(d) { return d.source.x; })
                                    .attr("y1", function(d) { return d.source.y; })
                                    .attr("x2", function(d) { return d.target.x; })
                                    .attr("y2", function(d) { return d.target.y; });
    nodes.attr("transform", function(d) { return "translate(" + d.x + "," + d.y + ")"; });

    edgepaths.attr('d', function(d) { var path='M '+d.source.x+' '+d.source.y+' L '+ d.target.x +' '+d.target.y;
                                        //console.log(d)
                                        return path});       
    // positioning of the label along the edge
    edgelabels.attr('transform',function(d,i){
                 if (d.target.x<d.source.x){
                   bbox = this.getBBox();
                   rx = bbox.x+bbox.width/2;
                   ry = bbox.y+bbox.height/2;
                   return 'rotate(180 '+rx+' '+ry+')';
                 }
                 else {
                   return 'rotate(0)';
                 }
    });
  });     
  // });  // not needed due to msg handler End of reading in JSON from file 
  // Double click to 'unfix' the node and have forces start to act on it again.
  function dblclick(d) {
    d3.select(this).classed("fixed", d.fixed = false);
  }
  // Set the "fixed" property of the dragged node to TRUE when a dragstart event is initiated,
  //   - removes "forces" from acting on that node and changing its position.
  function dragstart(d) {
    d3.select(this).classed("fixed", d.fixed = true);
  }
  }); // end of new function
+4
source share
1 answer

. ; 3 twoNodes.js

var dataset = message;
+1

All Articles