如何将数据驱动的d3JS图形与Shiny集成?
昨天我问了如何将带有自包含数据的d3js javacript文件带入Shiny以绘制强制网络图。现在我正在寻找下一步:server.R将读取一个JSON数据文件在图形中显示。我一直在尝试适应使用messageHandlers传递数据的此示例进入d3JS。这是超出我的专业知识,所以我寻找一些帮助。我很确定它是在messageHandler中的东西出了问题。
Yesterday I asked how to bring a d3js javacript file with self-contained data into Shiny to draw a force network graph. Now I am looking for the next step: server.R will read a JSON data file for display in the graph. I've been trying to adapt this example that uses messageHandlers to pass the data into the d3JS. This is beyond my expertise so I am looking for some help. I'm pretty sure it is in the messageHandler where things go awry.
我会很乐意发布完整的工作示例,因为这将导致我一个新的水平的理解R,Shiny和d3JS集成。 PS:是的,我看过networkD3和其他工具。我的d3JS图表比这里简单的例子复杂得多。 :)下一步也将包括使图形反应以选择在闪亮的输入,但我需要解决的第一个问题。
谢谢你!
Tim
I will gladly publish the full working example because this will lead me to a new level of understanding of R, Shiny and d3JS integration. PS: yes, I have looked at networkD3 and other tools. My d3JS charts are much more complex that the simple example here. :) Next steps will also include making the graph reactive to choosing inputs in Shiny, but I need this issue solved first of course. Thank you so much! Tim
ui.R - 按下按钮,接收图表!
ui.R - Push the button, receive graph!
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 - 当前读取两个节点及其链接。 IRL它将查询数据存储。
server.R - currently reads in two nodes and their link. IRL it would query a data store.
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 - 数据
twoNodes.JSON - the data
{
"nodes":[
{"id":0,"name":"Observations","group":"1"},
{"id":1,"name":"DataSet","group":"2"}
],
"edges":[
{"source":0,"target":1,"value":""}
]
}
twoNodes.css - 样式表
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 - 我试图利用的d3JS魔法
twoNodes.js - the d3JS magic I am trying to harness
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
它工作与一个小的修改; twoNodes.js中的第3行应为
You are fairly close. It works with a minor modification; Line 3 in twoNodes.js should be
var dataset = message;