// Get JSON data YFULL.js treeJSON = d3.json("tree.json.2019-08-29 23:44:19", function (error, treeData) { var HOR_DIST_PER_LEVEL = 1200; // 640 the horizontal sitance per node var VERT_DIST_PER_LEVEL = 400; // 160 var MIN_NODE_RADIUS = 3; // 3 Jari var MIN_NODE_AREA = MIN_NODE_RADIUS * MIN_NODE_RADIUS * Math.PI; var ZOOM_LEVEL = 0.01; var varToggle = true; // Tooltip var toolTip = d3.select(document.getElementById("toolTip")); // var header = d3.select(document.getElementById("head")); var toolTipText = d3.select(document.getElementById("toolTipText")); // Calculate total nodes, max label lengthh var totalNodes = 0; var maxLabelLength = 0; // variables for drag/drop var selectedNode = null; var draggingNode = null; // panning variables var panSpeed = 200; var panBoundary = 20; // Within 20px from edges will pan when dragging. // Misc. variables var i = 0; var duration = 750; var root; // size of the diagram var viewerWidth = $(document).width()-100; var viewerHeight = $(document).height(); var tree = d3.layout.tree() .size([viewerHeight, viewerWidth]); // define a d3 diagonal projection for use by the node paths later on. var diagonal = d3.svg.diagonal() .projection(function (d) { return [d.y, d.x]; }); // A recursive helper function for performing some setup by walking through all nodes function visit(parent, visitFn, childrenFn) { if (!parent) return; visitFn(parent); var children = childrenFn(parent); var totalNumLeaves = 0; if (children) { var count = children.length; //totalNumLeaves = totalNumLeaves + children.length for (var i = 0; i < count; i++) { totalNumLeaves = totalNumLeaves + visit(children[i], visitFn, childrenFn); } parent["totalNumLeaves"] = totalNumLeaves; return totalNumLeaves; } else { // we have a leaf node //parent["totalNumLeaves"] = totalNumLeaves; if (parent.citation) return 1; else { return 1; } // Original = 0, Jari Set all to be leaf nodes } } // Call visit function to establish maxLabelLength visit(treeData, function (d) { totalNodes++; maxLabelLength = Math.max(d.name.length, maxLabelLength); }, function (d) { //return d.children && d.children.length > 0 ? d.children : null; return d.children && d.children.length > 0 ? d.children : null; }); // sort the tree according to the node names function sortTree() { tree.sort(function (a, b) { return b.name.toLowerCase() < a.name.toLowerCase() ? 1 : -1; }); } // Sort the tree initially incase the JSON isnt in a sorted order. // sortTree(); // TODO: Pan function, can be better implemented. function pan(domNode, direction) { var speed = panSpeed; if (panTimer) { clearTimeout(panTimer); translateCoords = d3.transform(svgGroup.attr("transform")); if (direction == 'left' || direction == 'right') { translateX = direction == 'left' ? translateCoords.translate[0] + speed : translateCoords.translate[0] - speed; translateY = translateCoords.translate[1]; } else if (direction == 'up' || direction == 'down') { translateX = translateCoords.translate[0]; translateY = direction == 'up' ? translateCoords.translate[1] + speed : translateCoords.translate[1] - speed; } scaleX = translateCoords.scale[0]; scaleY = translateCoords.scale[1]; scale = zoomListener.scale(); svgGroup.transition().attr("transform", "translate(" + translateX + "," + translateY + ")scale(" + scale + ")"); d3.select(domNode).select('g.node').attr("transform", "translate(" + translateX + "," + translateY + ")"); zoomListener.scale(zoomListener.scale()); zoomListener.translate([translateX, translateY]); panTimer = setTimeout(function () { pan(domNode, speed, direction); }, 50); } } // Define the zoom function for the zoomable tree function zoom() { svgGroup.attr("transform", "translate(" + d3.event.translate + ")scale(" + d3.event.scale + ")"); } // define the zoomListener which calls the zoom function on the "zoom" event constrained within the scaleExtents var zoomListener = d3.behavior.zoom().scaleExtent([0.015, 2]).on("zoom", zoom); // Jari 0.1->0.05 function initiateDrag(d, domNode) { draggingNode = d; d3.select(domNode).select('.ghostCircle').attr('pointer-events', 'none'); d3.selectAll('.ghostCircle').attr('class', 'ghostCircle show'); d3.select(domNode).attr('class', 'node activeDrag'); svgGroup.selectAll("g.node").sort(function (a, b) { // select the parent and sort the paths if (a.id != draggingNode.id) return 1; // a is not the hovered element, send "a" to the back else return -1; // a is the hovered element, bring "a" to the front }); // if nodes has children, remove the links and nodes if (nodes.length > 1) { // remove link paths links = tree.links(nodes); nodePaths = svgGroup.selectAll("path.link") .data(links, function (d) { return d.target.id; }).remove(); // remove child nodes nodesExit = svgGroup.selectAll("g.node") .data(nodes, function (d) { return d.id; }).filter(function (d, i) { if (d.id == draggingNode.id) { return false; } return true; }).remove(); } // remove parent link parentLink = tree.links(tree.nodes(draggingNode.parent)); svgGroup.selectAll('path.link').filter(function (d, i) { if (d.target.id == draggingNode.id) { return true; } return false; }).remove(); dragStarted = null; } // define the baseSvg, attaching a class for styling and the zoomListener var baseSvg = d3.select("#tree-container").append("svg") .attr("width", viewerWidth) .attr("height", viewerHeight) .attr("class", "overlay") .call(zoomListener); // Define the drag listeners for drag/drop behaviour of nodes. dragListener = d3.behavior.drag() .on("dragstart", function (d) { if (d == root) { return; } dragStarted = true; nodes = tree.nodes(d); d3.event.sourceEvent.stopPropagation(); // its important that we suppress the mouseover event on the node being dragged. Otherwise it will absorb the mouseover event and the underlying node will not detect it d3.select(this).attr('pointer-events', 'none'); }) .on("drag", function (d) { if (d == root) { return; } if (dragStarted) { domNode = this; initiateDrag(d, domNode); } // get coords of mouseEvent relative to svg container to allow for panning relCoords = d3.mouse($('svg').get(0)); if (relCoords[0] < panBoundary) { panTimer = true; pan(this, 'left'); } else if (relCoords[0] > ($('svg').width() - panBoundary)) { panTimer = true; pan(this, 'right'); } else if (relCoords[1] < panBoundary) { panTimer = true; pan(this, 'up'); } else if (relCoords[1] > ($('svg').height() - panBoundary)) { panTimer = true; pan(this, 'down'); } else { try { clearTimeout(panTimer); } catch (e) { } } d.x0 += d3.event.dy; d.y0 += d3.event.dx; var node = d3.select(this); node.attr("transform", "translate(" + d.y0 + "," + d.x0 + ")"); updateTempConnector(); }).on("dragend", function (d) { if (d == root) { return; } domNode = this; if (selectedNode) { // now remove the element from the parent, and insert it into the new elements children var index = draggingNode.parent.children.indexOf(draggingNode); if (index > -1) { draggingNode.parent.children.splice(index, 1); } if (typeof selectedNode.children !== 'undefined' || typeof selectedNode._children !== 'undefined') { if (typeof selectedNode.children !== 'undefined') { selectedNode.children.push(draggingNode); } else { selectedNode._children.push(draggingNode); } } else { selectedNode.children = []; selectedNode.children.push(draggingNode); } // Make sure that the node being added to is expanded so user can see added node is correctly moved expand(selectedNode); // sortTree(); endDrag(); } else { endDrag(); } }); function endDrag() { selectedNode = null; d3.selectAll('.ghostCircle').attr('class', 'ghostCircle'); d3.select(domNode).attr('class', 'node'); // now restore the mouseover event or we wont be able to drag a 2nd time d3.select(domNode).select('.ghostCircle').attr('pointer-events', ''); updateTempConnector(); if (draggingNode !== null) { update(root); centerNode(draggingNode); draggingNode = null; } } // Helper functions for collapsing and expanding nodes. function collapse(d) { // jari if (d.children) { d._children = d.children; d._children.forEach(collapse); d.children = null; } } function expand(d) { if (d._children) { d.children = d._children; d.children.forEach(expand); d._children = null; } } var overCircle = function (d) { selectedNode = d; updateTempConnector(); }; var outCircle = function (d) { selectedNode = null; updateTempConnector(); }; // Function to update the temporary connector indicating dragging affiliation var updateTempConnector = function () { var data = []; if (draggingNode !== null && selectedNode !== null) { // have to flip the source coordinates since we did this for the existing connectors on the original tree data = [{ source: { x: selectedNode.y0, y: selectedNode.x0 }, target: { x: draggingNode.y0, y: draggingNode.x0 } }]; } var link = svgGroup.selectAll(".templink").data(data); link.enter().append("path") .attr("class", "templink") .attr("d", d3.svg.diagonal()) .attr('pointer-events', 'none'); link.attr("d", d3.svg.diagonal()); link.exit().remove(); }; // Function to center node when clicked/dropped so node doesnt get lost when collapsing/moving with large amount of children. function centerNode(source) { scale = zoomListener.scale(); x = -source.y0; y = -source.x0; x = x * scale + viewerWidth / 2; // /2 Jari y = y * scale + viewerHeight / 2; d3.select('g').transition() .duration(duration) .attr("transform", "translate(" + x + "," + y + ")scale(" + scale + ")"); zoomListener.scale(scale); zoomListener.translate([x, y]); } // Zoom into arbitrary node Jari function gotoNode(node) { x = -node.y0; y = -node.x0; x = x + viewerWidth / 2; // 2 Jari y = y + viewerHeight / 2; //2 Jari console.log("Zoom Level:" + x, y); scale = zoomListener.scale(); d3.select('g').attr("transform", "translate(" + x + "," + y + ")scale(1)"); zoomListener.scale(1); // 1 jari zoomListener.translate([x, y]); d3.selectAll('.nodeText') .filter(function (d) { return d === node; }) .attr('class', '.nodeText selected'); //.attr('style', 'fill:red'); //ColorNode(d); centerNode((node), zoomListener.scale(0.3)); // Jari 0.8 } // Toggle children function function toggleChildren(d) { if (d.children) { d._children = d.children; d.children = null; } else if (d._children) { d.children = d._children; d._children = null; } return d; } // Toggle children on click. function click(d) { if (d3.event.defaultPrevented) return; // click suppressed // d = toggleChildren(d); // Jari console.log('Node to toggle: ' + d.name); update(d); centerNode(d); } function update(source) { // Compute the new height, function counts total children of root node and sets tree height accordingly. // This prevents the layout looking squashed when new nodes are made visible or looking sparse when nodes are removed // This makes the layout more consistent. var levelWidth = [1]; var childCount = function (level, n) { if (n.children && n.children.length > 0) { if (levelWidth.length <= level + 1) levelWidth.push(0); levelWidth[level + 1] += n.children.length; n.children.forEach(function (d) { childCount(level + 1, d); }); } }; childCount(0, root); //var newHeight = d3.max(levelWidth) * 25; // 25 pixels per line var newHeight = d3.max(levelWidth) * VERT_DIST_PER_LEVEL; // 25 pixels per line tree = tree.size([newHeight, viewerWidth]); // Compute the new tree layout. //var nodes = tree.nodes(root).reverse(); var nodes = tree.nodes(root); var links = tree.links(nodes); // Set widths between levels based on maxLabelLength. nodes.forEach(function (d) { //d.y = (d.depth * (maxLabelLength * 10)); //maxLabelLength * 10px // alternatively to keep a fixed scale one can set a fixed depth per level // Normalize for fixed-depth by commenting out below line //d.y = (d.depth * 500); //500px per level. d.y = (d.depth * HOR_DIST_PER_LEVEL); //500px per level. }); // Update the nodes node = svgGroup.selectAll("g.node") .data(nodes, function (d) { return d.id || (d.id = ++i); }); // Enter any new nodes at the parents previous position. var nodeEnter = node.enter().append("g") // .call(dragListener) .attr("class", "node") .attr("transform", function (d) { return "translate(" + source.y0 + "," + source.x0 + ")"; }) // .on('click', click) .on("click", function (d) { console.log(varToggle); if(varToggle){ showInformation(d); varToggle = !true; console.log(varToggle); ColorLinks(d); }else{ toolTip.transition() // declare the transition properties to fade-out the div .duration(300) // it shall take 500ms .style("opacity", "0"); // and go all the way to an opacity of nil varInformation=""; varToggle = !false; ColorLinks(d); } }) //.on("clickOutside", function (d){ /*.on("click", function (d) { // when click circle, do the following if(!varToggle){ showInformation(d); varToggle = !true; console.log(varToggle); } //varToggle = !false; console.log(varToggle); });*/ nodeEnter.append("circle") .attr('class', 'nodeCircle') .attr("r", 0) .style("fill", function (d) { return d._children ? "lightsteelblue" : "#fff"; }); nodeEnter.append("text") .attr("x", function (d) { return d.children || d._children ? -10 : 10; }) .attr("dy", ".35em") .attr('class', 'nodeText') .attr("text-anchor", function (d) { return d.children || d._children ? "end" : "start"; }) .text(function (d) { // Jari - elaborated data to be replaced on nodes // YFULL TMRCA if(d.name.match('^N-xxx')) d.name = 'N-xxx' else return d.name; }) .style("fill-opacity", 0); // Jari nodeEnter.append("a") .attr("xlink:href", function (d) { /*if (d.name.match("^N-")){ // Jari varLast = location.href.indexOf("?"); varHash = d.name.indexOf("#")-1; return location.href.substr(0,varLast) + "?node=" + d.name; // d.name.substr(0,varHash); } else { varLast = location.href.indexOf("?"); varSpace = d.name.indexOf(" "); console.log(varSpace); return location.href.substr(0,varLast) + "?node=" + d.name.substr(0,varSpace); }*/ }) .append("rect") .attr("class", "clickable") .attr("y", -6) .attr("x", function (d) { return d.children || d._children ? -60 : 10; }) .attr("width", 200) //2*4.5) .attr("height", 12) .style("fill", "lightsteelblue") .style("fill-opacity", .0) // set to 1e-6 to hide ; // phantom node to give a radius around the node // Jari nodeEnter.append("circle") .attr("r", function (d){ if (d.ybp !== undefined) { {return 60;} }else{ // if (d.name.match(/Kinnunen/)){ return 40; } // Period 1 Jari } }) .attr("opacity", 0.5) // change this to zero to hide the target area .style("fill", function (d) { if ((d.ybp>15000) || (d.name.match(/^N-M231/)) ){ return "BlueViolet"; } // Period 1 Jari if ((d.ybp<15000) && (d.ybp>=10000) ){ return "Blue"; } // Period 2 if ((d.ybp<10000) && (d.ybp>=5000) || (d.name.match(/Kinnunen/)) ){ return "DodgerBlue"; } // Period 3 if ((d.ybp<5000) && (d.ybp>=4000) ){ return "Cyan"; } // Period 4 if ((d.ybp<4000) && (d.ybp>=3000) ){ return "Green"; } // Period 6 if ((d.ybp<3000) && (d.ybp>=2000) ){ return "LimeGreen"; } // Period 6 if ((d.ybp<2000) && (d.ybp>=1500) ){ return "LawnGreen"; } // Period 7 if ((d.ybp<1500) && (d.ybp>=1000) ){ return "Gold"; } // Period 8 if ((d.ybp<1000) && (d.ybp>=500) ){ return "Orange"; } // Period 9 if ((d.ybp<500) && (d.ybp>0) ){ return "Red"; } // Period 10 }) // Append images nodeEnter.append("svg:image") .attr("xlink:href", function(d) { if(d.name.match("Sapmi")){ return './images/SP.svg';} if(d.name.match("Ancient")){ return './images/SPADE.svg';} if(d.name.match("Armenia")){ return './images/AM.svg';} if(d.name.match("Austria")){ return './images/AT.svg';} if(d.name.match("Azerbaijan")){ return './images/AZ.svg';} if(d.name.match("Belarus")){ return './images/BY.svg';} if(d.name.match("Bosnia Herzegovina")){ return './images/BA.svg';} if(d.name.match("Bulgaria")){ return './images/BG.svg';} if(d.name.match("China")){ return './images/CN.svg';} if(d.name.match("Croatia")){ return './images/HR.svg';} if(d.name.match("Czech")){ return './images/CZ.svg';} if(d.name.match("Denmark")){ return './images/DK.svg';} if(d.name.match("England")){ return './images/ENG.svg';} if(d.name.match("Estonia")){ return './images/EE.svg';} if(d.name.match("Finland")){ return './images/FI.svg';} if(d.name.match("Germany")){ return './images/DE.svg';} if(d.name.match("Hungary")){ return './images/HU.svg';} if(d.name.match("India")){ return './images/IN.svg';} if(d.name.match("Iran")){ return './images/IR.svg';} if(d.name.match("Iraq")){ return './images/IQ.svg';} if(d.name.match("Ireland")){ return './images/IE.svg';} if(d.name.match("Japan")){ return './images/JP.svg';} if(d.name.match("Kazakhstan")){ return './images/KZ.svg';} if(d.name.match("Kyrgyzstan")){ return './images/KG.svg';} if(d.name.match("Latvia")){ return './images/LV.svg';} if(d.name.match("Lithuania")){ return './images/LT.svg';} if(d.name.match("Mongolia")){ return './images/MN.svg';} if(d.name.match("Montenegro")){ return './images/ME.svg';} if(d.name.match("Netherlands")){ return './images/NL.svg';} if(d.name.match("North-Korea")){ return './images/PRK.svg';} if(d.name.match("Norway")){ return './images/NO.svg';} if(d.name.match("Papua New Guinea")){ return './images/PG.svg';} if(d.name.match("Poland")){ return './images/PL.svg';} if(d.name.match("Romania")){ return './images/RO.svg';} if(d.name.match("Russia")){ return './images/RU.svg';} if(d.name.match("Scotland")){ return './images/SCT.svg';} if(d.name.match("Serbia")){ return './images/RS.svg';} if(d.name.match("Singapore")){ return './images/SG.png';} if(d.name.match("Slovakia")){ return './images/SK.svg';} if(d.name.match("South-Korea")){ return './images/KR.svg';} if(d.name.match("Sweden")){ return './images/SE.svg';} if(d.name.match("Turkey")){ return './images/TR.svg';} if(d.name.match("Ukraine")){ return './images/UA.svg';} if(d.name.match("United States")){ return './images/US.svg';} if(d.name.match("Uzbekistan")){ return './images/UZ.svg';} if(d.name.match("Unknown")){ return './images/Q.svg';} {return null;} } ) .attr("x", function(d) { return -56;}) .attr("y", function(d) { return -30;}) .attr("height", 60) .attr("width", 60); // Update the text to reflect whether node has children or not. node.select('text') .attr("x", function (d) { var radius = computeNodeRadius(d); return d.children || d._children ? (-radius - 5) : (radius + 5); }) .attr("text-anchor", function (d) { return d.children || d._children ? "end" : "start"; }) .text(function (d) { varCharPosition = d.name.indexOf("#"); // Jari if(d.name.indexOf('#') === -1) // If d.name does not contain "#" return d.name; // .substr(varCharPosition); // Show full string else return d.name.substr(0, varCharPosition); // Otherwise everything before "#" // else return d.name.substr(0, varCharPosition); // Otherwise everything before "#" }); // Change the circle fill depending on whether it has children and is collapsed node.select("circle.nodeCircle") .attr("r", function (d) { return computeNodeRadius(d); }) .style("fill", function (d) { // Color SNPs included in SNP Pack /*varNodeColor = "Black"; if(d.name.match(/^X-/)) return d._children ? "Gainsboro" : "#fff"; else if(d.name.match(/^Scandinavia/)) return d._children ? "Gainsboro" : "Crimson"; else if(d.name.match(/^Savonia/)) return d._children ? "Gainsboro" : "Crimson"; else if(d.name.match(/^Baltic-Nordic/)) return d._children ? "Gainsboro" : "Crimson"; else if(d.name.match(/^North-Baltic/)) return d._children ? "Gainsboro" : "Crimson"; else if(d.name.match(/^South-Baltic/)) return d._children ? "Gainsboro" : "Crimson"; else if(d.name.match(/^Lapponia/)) return d._children ? "Gainsboro" : "Crimson"; else if(d.name.match(/^Carelia/)) return d._children ? "Gainsboro" : "Crimson"; else if(d.name.match(/^Balkan/)) return d._children ? "Gainsboro" : "Crimson"; else if(d.name.match(/^N-M231/)) return d._children ? 'Gainsboro' : varNodeColor; else if(d.name.match(/^N-L735/)) return d._children ? 'Gainsboro' : varNodeColor; else if(d.name.match(/^N-F1206/)) return d._children ? 'Gainsboro' : varNodeColor; else if(d.name.match(/^N-L395/)) return d._children ? 'Gainsboro' : varNodeColor; else if(d.name.match(/^N-M178/)) return d._children ? 'Gainsboro' : varNodeColor; else if(d.name.match(/^N-L550/)) return d._children ? 'Gainsboro' : varNodeColor; else if(d.name.match(/^N-Z1936/)) return d._children ? 'Gainsboro' : varNodeColor; else if(d.name.match(/^N-CTS8445/)) return d._children ? 'Gainsboro' : varNodeColor; else if(d.name.match(/^N-CTS3451/)) return d._children ? 'Gainsboro' : varNodeColor; else if(d.name.match(/^N-CTS2262/)) return d._children ? 'Gainsboro' : varNodeColor; else if(d.name.match(/^N-CTS6380/)) return d._children ? 'Gainsboro' : varNodeColor; else if(d.name.match(/^N-CTS11713/)) return d._children ? 'Gainsboro' : varNodeColor; else if(d.name.match(/^N-CTS1350/)) return d._children ? 'Gainsboro' : varNodeColor; else if(d.name.match(/^N-L591/)) return d._children ? 'Gainsboro' : varNodeColor; else if(d.name.match(/^N-L551/)) return d._children ? 'Gainsboro' : varNodeColor; else if(d.name.match(/^N-P189/)) return d._children ? 'Gainsboro' : varNodeColor; else if(d.name.match(/^N-L1025/)) return d._children ? 'Gainsboro' : varNodeColor; else if(d.name.match(/^N-L1022/)) return d._children ? 'Gainsboro' : varNodeColor; else if(d.name.match(/^N-L732/)) return d._children ? 'Gainsboro' : varNodeColor; else if(d.name.match(/^N-L731/)) return d._children ? 'Gainsboro' : varNodeColor; else if(d.name.match(/^N-L708/)) return d._children ? 'Gainsboro' : varNodeColor; else if(d.name.match(/^N-CTS2929/)) return d._children ? 'Gainsboro' : varNodeColor; else if(d.name.match(/^N-Z1935/)) return d._children ? 'Gainsboro' : varNodeColor; else if(d.name.match(/^N-Z1925/)) return d._children ? 'Gainsboro' : varNodeColor; else if(d.name.match(/^N-Z1941/)) return d._children ? 'Gainsboro' : varNodeColor; else if(d.name.match(/^N-Z1940/)) return d._children ? 'Gainsboro' : varNodeColor; else if(d.name.match(/^N-L1034/)) return d._children ? 'Gainsboro' : varNodeColor; else if(d.name.match(/^N-L1419/)) return d._children ? 'Gainsboro' : varNodeColor; else if(d.name.match(/^N-Y3185/)) return d._children ? 'Gainsboro' : varNodeColor; else if(d.name.match(/^N-Y3192/)) return d._children ? 'Gainsboro' : varNodeColor; else if(d.name.match(/^N-L729/)) return d._children ? 'Gainsboro' : varNodeColor; else if(d.name.match(/^N-CTS9976/)) return d._children ? 'Gainsboro' : varNodeColor; else if(d.name.match(/^N-Y5003/)) return d._children ? 'Gainsboro' : varNodeColor; else if(d.name.match(/^N-Y5005/)) return d._children ? 'Gainsboro' : varNodeColor; else if(d.name.match(/^N-Y5004/)) return d._children ? 'Gainsboro' : varNodeColor; else if(d.name.match(/^N-L1027/)) return d._children ? 'Gainsboro' : varNodeColor; else if(d.name.match(/^N-Y5580/)) return d._children ? 'Gainsboro' : varNodeColor; else if(d.name.match(/^N-M2783/)) return d._children ? 'Gainsboro' : varNodeColor; else if(d.name.match(/^N-Y5576/)) return d._children ? 'Gainsboro' : varNodeColor; else if(d.name.match(/^N-CTS8565/)) return d._children ? 'Gainsboro' : varNodeColor; else if(d.name.match(/^N-CTS7189/)) return d._children ? 'Gainsboro' : varNodeColor; else if(d.name.match(/^N-Z1939/)) return d._children ? 'Gainsboro' : varNodeColor; else if(d.name.match(/^N-VL62/)) return d._children ? 'Gainsboro' : varNodeColor; else if(d.name.match(/^N-Z1933/)) return d._children ? 'Gainsboro' : varNodeColor; else if(d.name.match(/^N-Z4770/)) return d._children ? 'Gainsboro' : varNodeColor; else if(d.name.match(/^N-Z5038/)) return d._children ? 'Gainsboro' : varNodeColor; else if(d.name.match(/^N-Z4786/)) return d._children ? 'Gainsboro' : varNodeColor; else if(d.name.match(/^N-Z4784/)) return d._children ? 'Gainsboro' : varNodeColor; else if(d.name.match(/^N-Z5892/)) return d._children ? 'Gainsboro' : varNodeColor; else if(d.name.match(/^N-Y4338/)) return d._children ? 'Gainsboro' : varNodeColor; else if(d.name.match(/^N-Y4339/)) return d._children ? 'Gainsboro' : varNodeColor; else if(d.name.match(/^N-Y4374/)) return d._children ? 'Gainsboro' : varNodeColor; else if(d.name.match(/^N-Y5611/)) return d._children ? 'Gainsboro' : varNodeColor; else if(d.name.match(/^N-F1008/)) return d._children ? 'Gainsboro' : varNodeColor; else if(d.name.match(/^N-M128/)) return d._children ? 'Gainsboro' : varNodeColor; else if(d.name.match(/^N-Y7300/)) return d._children ? 'Gainsboro' : varNodeColor; else if(d.name.match(/^N-Y7795/)) return d._children ? 'Gainsboro' : varNodeColor; else if(d.name.match(/^N-Y9022/)) return d._children ? 'Gainsboro' : varNodeColor; else if(d.name.match(/^N-Y9454/)) return d._children ? 'Gainsboro' : varNodeColor; else if(d.name.match(/^N-Z5893/)) return d._children ? 'Gainsboro' : varNodeColor; else if(d.name.match(/^N-Y4706/)) return d._children ? 'Gainsboro' : varNodeColor; else if(d.name.match(/^N-Y4703/)) return d._children ? 'Gainsboro' : varNodeColor; else if(d.name.match(/^N-Y5743/)) return d._children ? 'Gainsboro' : varNodeColor; else if(d.name.match(/^N-Y13475/)) return d._children ? 'Gainsboro' : varNodeColor; else if(d.name.match(/^N-Y13850/)) return d._children ? 'Gainsboro' : varNodeColor; else if(d.name.match(/^N-Y13851/)) return d._children ? 'Gainsboro' : varNodeColor; else if(d.name.match(/^N-Y11631/)) return d._children ? 'Gainsboro' : varNodeColor; else if(d.name.match(/^N-Y4702/)) return d._children ? 'Gainsboro' : varNodeColor; else if(d.name.match(/^N-Z16981/)) return d._children ? 'Gainsboro' : varNodeColor; else if(d.name.match(/^N-Z16980/)) return d._children ? 'Gainsboro' : varNodeColor; else if(d.name.match(/^N-YP1143/)) return d._children ? 'Gainsboro' : varNodeColor; else if(d.name.match(/^N-Y11882/)) return d._children ? 'Gainsboro' : varNodeColor; else if(d.name.match(/^N-Y12103/)) return d._children ? 'Gainsboro' : varNodeColor; else if(d.name.match(/^N-Y13852/)) return d._children ? 'Gainsboro' : varNodeColor; else if(d.name.match(/^N-Y13973/)) return d._children ? 'Gainsboro' : varNodeColor; else if(d.name.match(/^N-Y13974/)) return d._children ? 'Gainsboro' : varNodeColor; else if(d.name.match(/^N-Y13977/)) return d._children ? 'Gainsboro' : varNodeColor; else if(d.name.match(/^N-Y6075/)) return d._children ? 'Gainsboro' : varNodeColor; else if(d.name.match(/^N-Y6076/)) return d._children ? 'Gainsboro' : varNodeColor; else if(d.name.match(/^N-Y6077/)) return d._children ? 'Gainsboro' : varNodeColor; else if(d.name.match(/^N-Y6374/)) return d._children ? 'Gainsboro' : varNodeColor; else if(d.name.match(/^N-Y6599/)) return d._children ? 'Gainsboro' : varNodeColor; else if(d.name.match(/^N-Y7297/)) return d._children ? 'Gainsboro' : varNodeColor; else if(d.name.match(/^N-YP1141/)) return d._children ? 'Gainsboro' : varNodeColor; else if(d.name.match(/^N-CTS12908/)) return d._children ? 'Gainsboro' : varNodeColor; else if(d.name.match(/^N-CTS12473/)) return d._children ? 'Gainsboro' : varNodeColor; else if(d.name.match(/^N-L665/)) return d._children ? 'Gainsboro' : varNodeColor; else if(d.name.match(/^N-L727/)) return d._children ? 'Gainsboro' : varNodeColor; else if(d.name.match(/^N-P43/)) return d._children ? 'Gainsboro' : varNodeColor; else if(d.name.match(/^N-PF3452/)) return d._children ? 'Gainsboro' : varNodeColor; else if(d.name.match(/^N-M2019/)) return d._children ? 'Gainsboro' : varNodeColor; else if(d.name.match(/^N-Y15159/)) return d._children ? 'Gainsboro' : varNodeColor; else if(d.name.match(/^N-Z35267/)) return d._children ? 'Gainsboro' : varNodeColor; else if(d.name.match(/^N-CTS4642/)) return d._children ? 'Gainsboro' : varNodeColor; else if(d.name.match(/^N-F2905/)) return d._children ? 'Gainsboro' : varNodeColor; else if(d.name.match(/^N-FGC13372/)) return d._children ? 'Gainsboro' : varNodeColor; else if(d.name.match(/^N-PR3050/)) return d._children ? 'Gainsboro' : varNodeColor; else if(d.name.match(/^N-Y10931/)) return d._children ? 'Gainsboro' : varNodeColor; else if(d.name.match(/^N-VL97/)) return d._children ? 'Gainsboro' : varNodeColor; else if(d.name.match(/^N-VL64/)) return d._children ? 'Gainsboro' : varNodeColor; else if(d.name.match(/^N-VL65/)) return d._children ? 'Gainsboro' : varNodeColor; else if(d.name.match(/^N-VL67/)) return d._children ? 'Gainsboro' : varNodeColor; else return d._children ? "Gainsboro" : "AliceBlue"; // Jari */ }); // Transition nodes to their new position. var nodeUpdate = node.transition() .duration(duration) .attr("transform", function (d) { return "translate(" + d.y + "," + d.x + ")"; }); // Fade the text in nodeUpdate.select("text") .style("fill-opacity", 1); // Transition exiting nodes to the parents new position. var nodeExit = node.exit().transition() .duration(duration) .attr("transform", function (d) { return "translate(" + source.y + "," + source.x + ")"; }) .remove(); nodeExit.select("circle") .attr("r", 0); nodeExit.select("text") .style("fill-opacity", 0); // Update the links var link = svgGroup.selectAll("path.link") .data(links, function (d) { return d.target.id; }); // Enter any new links at the parents previous position. link.enter().insert("path", "g") .attr("class", function (d) { return "link"; /* */ }) .attr("d", function (d) { var o = { x: source.x0, y: source.y0 }; return diagonal({ source: o, target: o }); }) .style("stroke-width", function (d, i) { return computeNodeRadius(d.target) * 1.2; // Jari }) .style("stroke", function (d) { // Jari Change color of defined node names if(d.target.name.match(/^N-/)) return "Silver"; if(d.target.name.match(/^Savonia/) || d.target.name.match(/^Lapponia/) || d.target.name.match(/^Carelia/) || d.target.name.match(/^Baltic-Nordic/) || d.target.name.match(/^North-Baltic/) || d.target.name.match(/^South-Baltic/) || d.target.name.match(/^Scandinavia/) || d.target.name.match(/^Balkan/)) return "Silver"; /*if (d.target.ybp>15000){ return "BlueViolet"; } // Jari if ((d.target.ybp<15000) && (d.target.ybp>=10000) ){ return "Blue"; } if ((d.target.ybp<10000) && (d.target.ybp>5000) ){ return "DeepSkyBlue"; } if ((d.target.ybp<5000) && (d.target.ybp>2500) ){ return "Cyan"; } if ((d.target.ybp<2500) && (d.target.ybp>1500) ){ return "LawnGreen"; } if ((d.target.ybp<1500) && (d.target.ybp>=1000) ){ return "Gold"; } if ((d.target.ybp<1000) && (d.target.ybp>=500) ){ return "Orange"; } if ((d.target.ybp<500) && (d.target.ybp>0) ){ return "Red"; }*/ else return "DodgerBlue"; }) .style("stroke-opacity", function (d) { var ret = (1 - ((d.target.depth + 1) / 200)) // Color fade Jari //if (d.target[spendField] <= 0) ret=0; return ret; }) // Transition links to their new position. link.transition() .duration(duration) .attr("d", diagonal); // Transition exiting nodes to the parents new position. link.exit().transition() .duration(duration) .attr("d", function (d) { var o = { x: source.x, y: source.y }; return diagonal({ source: o, target: o }); }) .remove(); // Stash the old positions for transition. nodes.forEach(function (d) { d.x0 = d.x; d.y0 = d.y; }); // the area of the circle should be proportional to the // number of leaves // A = pi * r*r // r = sqrt(A/pi) function computeNodeRadius(d) { if (d.totalNumLeaves) { //console.log(d.totalNumLeaves); return (Math.sqrt((MIN_NODE_AREA * (d.totalNumLeaves + 1)) / Math.PI)) } return MIN_NODE_RADIUS; } function showInformation(d) { // jari varSNP = d.name.slice(2); // Get SNP without haplogroup varYBP = ''; varSearch = ''; varInformation=""; varLinkEmoji = String.fromCodePoint(0x1F517); // "\u1F517"; // "\u1F9EC"; // "\u1F517"; varDNALinkEmoji = String.fromCodePoint(0x1F9EC); // "\u1F517"; // "\u1F9EC"; // "\u1F517"; varListEmoji = String.fromCodePoint(0x1F4CB); // U+1F4CB varSubmitEmoji = String.fromCodePoint(0x270F); // U+1F4CB varMapEmoji = String.fromCodePoint(0x1F5FA); // U+1F5FA // ToolTip section ****************************************************************************************************************************** if (d.name.match("^N-")){ // N- ------------------------------------------------------------------------------------------------------------------- if (d.name.match("^N-CTS8565")){ varInformation = "Informative STR: YCAII=18-18"; } // Jari if (d.name.match("^N-VL62")){ varInformation = "Informative STR: YCAII=18-20"; } toolTip.transition() .duration(100) .style("opacity", 1); if (d.ybp !== undefined){ varYBP = d.ybp; }else{ varYBP = 'Not found on by YFULL. YBP less than ' + d.parent.ybp; } toolTip.html( '