So I've generated a JSON file with four tiers and I used D3.js's collapsing boxes format (https://bl.ocks.org/swayvil/b86f8d4941bdfcbfff8f69619cd2f460) to create a visualization.
The problem I am encountering is that because the JSON file is so large, the visualization won't even load into the HTML and even when it does, the boxes in the visualization lag and don't collapse as they should.
Therefore, I want to create a search form using Select2's dropdown box in HTML, so that users can enter a node/tier 1 value and the visualization will appear with only that specific sector shown. Since I am not good with Javascript I am finding quite hard to create such search functionality. I have assigned ID numbers to each value in the JSON but I'm not sure how to approach it further (and if this is even the correct direction to go in).
Below is a sample of my JSON code. So for example, if a user enters Lawyer and McDonald's, I want the visualization show only that node and it's children. I apologize if I'm unclear but any help would be great. Thanks so much!
{
"tree": {
"name": "Top Level",
"children": [
{
"name": "[('Lawyer', 'McDonald's')]",
"children": [
{
"name": "[('Doctor', 'Wendy's')]",
"percentage": "10%",
"duration": 5,
"children": [
{
"name": "[('Nurse', 'NYU')]",
"percentage": "1%",
"duration": 5,
"children": [
{
"name": "[('Pharmacist', 'LIU')]",
"percentage": "4%",
"duration": 5,
"id": "1.1.1.1"
},
{
"name": "[('PA', 'Wagner')]",
"percentage": "4%",
"duration": 5,
"id": "1.1.1.2"
}
],
"id": "1.1.1"
},
{
"name": "[('Surgeon', 'Harvard')]",
"percentage": "1%",
"duration": 3,
"children": [
{
"name": "[('Dentist', 'Buffalo')]",
"percentage": "1%",
"duration": 4,
"id": "1.1.2.1"
}
],
"id": "1.1.2"
}
],
"id": "1.1"
}
],
"id": "1"
},
{
There are a few things to change before you can actually make the D3 visualization work properly. Here are they.
(1) The data structure of the json doesn't match with the D3 code for displaying it. If you'd like to show the json data in the collapsing boxes properly adjust the below part of the code. d here corresponds with your children object and has properties; name, percentage, and duration
.append('xhtml').html(function(d) {
return '<div style="width: '
+ (rectNode.width - rectNode.textMargin * 2) + 'px; height: '
+ (rectNode.height - rectNode.textMargin * 2) + 'px;" class="node-text wordwrap">'
+ '<b>' + d.name + '</b><br><br>'
+ '<b>Percentage: </b>' + d.percentage + '<br>'
+ '<b>Duration: </b>' + d.duration + '<br>'
+ '</div>';
});
(2) Your data doesn't contain the following which is responsible for displaying the arrows properly when collapsing items into a parent box. This might be the case why the animation is not as it should.
"link" : {
"name" : "Link node 1 to 2.3",
"nodeName" : "NODE NAME 2.3",
"direction" : "ASYN"
},
(3) Here's the addition needed to implement the select2 drop-down box with all main branches.
HTML
<select class="js-example-basic-single" name="state">
JavaScript
$(document).ready(function() {
// map over all children in the main json branch
var dropdownData = json.tree.children.map(function(branch, i) {
return {
id: i,
text: branch.name,
}
});
// Add all branches to select2.
$('.js-example-basic-single').select2({
data: dropdownData
});
// Add change event listener. When an option is selected, clear the SVG and run treeBoxes function again.
$('.js-example-basic-single').change(function(e) {
$("#tree-container").empty();
var selectedBranch = e.target.value;
treeBoxes(null, json.tree.children[selectedBranch]);
});
});
Here's a live working example on JSFiddle that still needs some tweaking but it'll help you in the right direction.
NOTE: To make this example work, I've added the json in the html document instead of loading data from a file.
Related
I have a working d3.js (v 4.0) hierarchical tree. Each level in the tree structure has a type parameter field determined in the process of creating the JSON from a MySQL database.
An example of the JSON is at the end.
I want to add another data/parameter field dynamically in the d3.js script where nodes d.type == 'unit'.
That data/parameter field can be called dv.
I would need to do a separate query on the unit table for this in another script, then iterate through those nodes, where nodes d.type == 'unit', for example to change the node's appearance if dv equals something.
How should this be done?
E.g. from
d3.json("json/data.json", function(treeData) {
Example JSON:
"name": "1.1 Ensure the appropriateness of sample collection procedures",
"column_to_sort_by": null,
"type": "program_outcome",
"children": [
{
"name": "PATH5121 Clinical Biochemistry",
"column_to_sort_by": "PATH5121 Clinical Biochemistry",
"children": [
{
"name": "Learning Events - PATH5121",
"column_to_sort_by": null,
"type": "unit_group"
},
{
"name": "Assessments - PATH5121",
"column_to_sort_by": null,
"type": "unit_group"
}
],
"type": "unit"
},
My goal here is to have the childless nodes contain hyperlinks. This is the D3 plugin I'm basing things off of: https://github.com/deltoss/d3-mitch-tree
Image Example
I'm newer to JS and JSON so I'm having difficulties on figuring out how to proceed, especially since there's little to refer to in regard to hyperlinks & JSON. If there's a better way to go about this, I'm certainly open to new ideas.
Thank you in advance
<script src="https://cdn.jsdelivr.net/gh/deltoss/d3-mitch-tree#1.0.2/dist/js/d3-mitch-tree.min.js"></script>
<script>
var data = {
"id": 1,
"name": "Animals",
"type": "Root",
"description": "A living that feeds on organic matter",
"children": [
{
"id": 6,
"name": "Herbivores",
"type": "Type",
"description": "Diet consists solely of plant matter",
"children": [
{
"id": 7,
"name": "Angus Cattle",
"type": "Organism",
"description": "Scottish breed of black cattle",
"children": []
},
{
"id": 8,
"name": "Barb Horse",
"type": "Organism",
"description": "A breed of Northern African horses with high stamina and hardiness. Their generally hot temperament makes it harder to tame.",
"children": []
}
]
}
]
};
var treePlugin = new d3.mitchTree.boxedTree()
.setData(data)
.setElement(document.getElementById("visualisation"))
.setMinScale(0.5)
.setAllowZoom(false)
.setIdAccessor(function(data) {
return data.id;
})
.setChildrenAccessor(function(data) {
return data.children;
})
.setBodyDisplayTextAccessor(function(data) {
return data.description;
})
.setTitleDisplayTextAccessor(function(data) {
return data.name;
})
.initialize();
</script>
Chain this method before .initialize():
.on("nodeClick", function(event, index, arr) {
if (!event.data.children.length) {
console.log('you clicked a child-less item', event.data);
}
})
Inside the condition, event.data is the clicked childless item. Feel free to place URLs inside those objects and use those URLs to navigate away.
Taken from the repo's /examples folder, where I found one named Advanced example with Node Click Event.
According to the comment on the same page, you can also achieve this using the options syntax:
/* const tree = [
place your data here...
]; // */
new d3.mitchTree.boxedTree({
events: {
nodeClick({ data }) {
if (!data.children.length) {
console.log('You clicked a childless item', data);
}
}
}
})
.setData(tree)
// ...rest of chain
.initialize();
I'm having a radial graph showing two levels of nodes. On clicking a node it is possible to add a sub graph with calling the sum() function. Everything works fine except setting individual color for the newly added edges.
Does anybody have ever tried to load sub graphs with individual edge colors or have a hint what I'm doing wrong?
Here I'm getting and adding the sub graph:
subtree = getSubtree(node.id);
//perform animation.
subtree.success(function(data){
rg.op.sum(data, {
type: 'fade:seq',
fps: 40,
duration: 1000,
hideLabels: false,
});
});
I've checked also the loaded data but for me it seems to be totally equal. I've also loaded the same data into the initial graph instead of the sub graph and then it was colored correct. Nevertheless here is some test data which is the result of the function getSubtree (the id "placeholder" matches the id of the existing where the sub graph should be added):
{
"id": "placeholder1",
"name": "country",
"children": [{
"id": "2_3mSV~_scat_1",
"name": "hyponym",
"children": [{
"children": [],
"adjacencies": {
"nodeTo": "2_3mSV~_scat_1",
"data": {
"$color": "#29A22D"
}
},
"data": {
"$color": "#29A22D"
},
"id": "3_58z3q_sc_174_6",
"name": "location"
}],
"data": {
"$type": "star",
"$color": "#666666"
},
"adjacencies": [{
"nodeTo": "3_58z3q_sc_174_6",
"data": {
"$color": "#29A22D"
}
}]
}]
}
I finally found the problem in the framework itself...
When calling the construct function inside the call of sum() which is actually adding the subtree then the data object containing information about the adjacence's individual visualization is not used for adding the new adjacence. Therefore I changed the code manually (this for loop is the new version of the existing for loop inside the construct() function):
for(var i=0, ch = json.children; i<ch.length; i++) {
//CUSTOM CODE: GET DATA OF THIS ADJACENCE
data = null;
if(ch[i].adjacencies[0]==undefined){
data = ch[i].adjacencies.data;
}
else{
data = ch[i].adjacencies.data;
}
ans.addAdjacence(json, ch[i], data);
arguments.callee(ans, ch[i]);
//CUSTOM CODE END
}
So I'm having a problem accessing my json when its in a nested array. I previously had json set up like this with just one array and my .$each function worked perfectly. However I'm having trouble modifying it for this.
Json:
{
"tcontent": [{
"Name": "Septicaemia",
"url":"<a>",
"image":"<div class='grid' style='background-image:url(img/anatomy/septicaemia.jpg);'>",
"Variations": [{
"condition":"Community-acquired",
"organisms":"Staph. aureus",
"antimicrobial":"Flucloxacillin ",
"alternative":"(non anaphylaxis): ",
"comments": "Perform full septic screen."
}, {
"Condition":"Community-acquired if intra- abdominal source suspected",
"Organisms":"Predominantly Gram negatives and anaerobes Enterococci may also feature",
"Antimicrobial":"Co-amoxiclav 1.2g iv tds",
"Comments":"Perform full septic screen"
}, {
"Condition":"Healthcare-associated",
"Organisms":"Varies",
"Antimicrobial":"Piperacillin",
"Alternative":"Seek advice from Consultant Microbiologist",
"Comments":"Always"
}]
}, {
"Name": "Infective Endocarditis (IE) (pending blood culture results)",
"url":"<a>",
"image":"<div class='grid' style='background-image:url(img/anatomy/endocarditis.jpg);'>"
}, {
"Name": "Central Nervous System Infections",
"url":"<a>",
"image":"<div class='grid' style='background-image:url(img/anatomy/cns.jpg);'>"
}, {
"Name": "Skin and Soft Tissue Infections",
"url": "<a>",
"image":"<div class='grid' style='background-image:url(img/anatomy/skin.jpg);'>"
}, {
"Name": "Diabetic patients with foot infections",
"url": "<a>",
"image":"<div class='grid' style='background-image:url(img/anatomy/foot.jpg);'>"
}, {
"Name": "Bone and Joint Infections",
"url": "<a>",
"image":"<<div class='grid' style='background-image:url(img/anatomy/bone.jpg);'>"
}, {
"Name": "Intravascular Line Infections",
"url": "<a>",
"image":"<div class='grid' style='background-image:url(img/anatomy/intravascular.jpg);'>"
}, {
"Name": "Urinary Tract Infections",
"url": "<a>",
"image":"<div class='grid' style='background-image:url(img/anatomy/urinary.jpg);'>"
}, {
"Name": "Respiratory Tract Infections",
"url": "<a>",
"image":"<div class='grid' style='background-image:url(img/anatomy/respiratory.jpg);'>"
}, {
"Name": "Gastrointestinal Infections",
"url": "<a>",
"image":"<div class='grid' style='backgroundimage:url(img/anatomy/gastrointestinal.jpg);'>"
}]
}
Here's my javascript to access it.
$(function (){
var imp = "Json/therapy.json"
$.getJSON(imp, function(data) {
var info = "<br>";
$.each(data.tcontent, function(i, item) {
if(item.Name=='Septicaemia'){
var search = item.Variations;
$.each(item.Variations, function(j, subitem) {
info += subitem.condition + subitem.organisms + subitem.antimicrobial + subitem.alternative + subitem.comments
});
$(info).appendTo(".menu");
//alert(item)
};
});
});
});
I've tried a many variations on the var search but nothing seems to be working. I researched a lot of similar problems to this and I've been stuck on this for too long. Any light that can be shed on the situation would be much appreciated!
2 reasons why it doesn't work.
First of all javascript is case sensitive, Your variations differ.
subitem.condition fails on :
"Condition":"Community-acquired if intra- abdominal source suspected",
"Organisms":"Predominantly Gram negatives and anaerobes Enterococci may also feature",
"Antimicrobial":"Co-amoxiclav 1.2g iv tds",
"Comments":"Perform full septic screen"
So change "Condition" to "condition", etc.etc.
second reason is the
Change $(info).appendTo(".menu"); to $(".menu").append(info);
Why?
$(".menu").append(info) Will just paste the string in the selected DOM element.
But you use
$(info)... and jquery does all kinds of fancy stuff now.
It tries to either use it as DOM selector, or create a new element.
Because your info starts with <br> $(info) tries to create a DOM element and it removes all text. Leaving just <br> because br cannot contain content.
Try to remove the initial <br> then you will see following error:
Uncaught Error: Syntax error, unrecognized expression:Community-acquiredStaph. aureusFlucloxacillin...
For example if you would type $("hahaha") , Jquery will try to find the tag <hahaha>, So when you remove the <br> your $(info) is looking for the tag <Community-acquiredStaph. aureusFlucloxacillin...>.
But because your string would then contain weird characters like "-()." It will fail. Hence the above error.
So you can only add html like this:
$("<span>hahah</span>").appendTo($(".menu"));
Or use selector
$("#myDiv").appendTo($(".menu"));
An example when $(info).appendTo(".menu"); working is:
$.each(data.tcontent, function(i, item) {
if(item.Name=='Septicaemia'){
var search = item.Variations;
$.each(item.Variations, function(j, subitem) {
var info = "<p>" + subitem.condition + subitem.organisms + subitem.antimicrobial + subitem.alternative + subitem.comments + "</p>";
$(info).appendTo(".menu");
});
}
});
Using the following json:
http://pastebin.com/Bzpix1ai
//treeview source
function populateTreeView(search) {
debugger;
var tree = $("#tvwResults").kendoTreeView({
dataTextField: [{
text: search.columnName,
items: [{
text: "activemeters"
}]
}],
select: function (e) {
console.log("Selecting ", e.node)
},
animation: {
expand: {
effects: "fadeIn expandVertical",
duration: 600
}
}
}).data("kendoTreeView");
$.getJSON("http://127.0.0.2:6080/arcgis/rest/services/WW/WW2/MapServer/exts/RestSOE/Search%20Query?columnName=" + search.columnName + "&operand=" + search.operand + "&searchVal=" + search.searchVal + "&f=", function (data) {
tree.dataSource.data(data);
});
};
I'm really missing something here. I can see the results in the root node, showing two records, however, the "activemeters" child node isn't showing up. I'm stumbling and thankful for help. Quite a setback hoping to push these results into a pivot grid using KendoUI but the control is non-existent. I just need two columns, first listing the table column names and the second showing the details. SO grateful for your help, learning alot here.
The JSON you showed isn't quite enough to tell how your hierarchy works, since that is just 1 node at 1 level, but the dataTextField that you have defined is incorrect. When it is specified as an array, it is supposed to be an array of strings that tells the treeview what field to use at each level of depth as the display name for the node.
I think you want something like this:
var tree = $("#tvwResults").kendoTreeView({
dataTextField: ["Account Num", "activemeters"],
select: function (e) {
console.log("Selecting ", e.node)
},
animation: {
expand: {
effects: "fadeIn expandVertical",
duration: 600
}
}
}).data("kendoTreeView");
tree.dataSource.data([
{
"Account Num": "210663845",
"Address": "9 COUNTRY RD",
"City": "HAMDENEE",
"Name_1": "ANDREW SMITH",
"Name_2": "",
"Street": "GREEN ST",
"Street Num": "25",
"items": [
{"activemeters": "T30619-00T|30078309"}
]
}
]);