gojs
Version:
Interactive diagrams, charts, and graphs, such as trees, flowcharts, orgcharts, UML, BPMN, or business diagrams
469 lines (441 loc) • 19.8 kB
HTML
<html>
<head>
<meta charset="UTF-8">
<title>Data Visualization GoJS Sample</title>
<meta name="description" content="Interactive visualization of multi-dimensional data with HTML tooltips." />
<meta name="viewport" content="width=device-width, initial-scale=1">
<!-- Copyright 1998-2020 by Northwoods Software Corporation. -->
<!--
The div needs an explicit size or else we won't see anything.
Lets also add a border to help see the edges.
-->
<style type="text/css">
#myDiagramDiv {
border: solid 1px black;
width: 400px;
height: 400px;
margin-right: 12px;
float: left;
background-color: whitesmoke;
}
#controls {
border: solid 1px gray;
width: 250px;
background: #ffffff;
padding: 5px;
float: left;
}
#infoBoxHolder {
z-index: 300;
position: absolute;
left: 5px;
}
#infoBox {
border: 1px solid #999;
padding: 8px;
background-color: whitesmoke;
opacity: 0.9;
position: relative;
width: 170px;
font-family: arial, helvetica, sans-serif;
font-weight: bold;
font-size: 11px;
}
/* this is known as the "clearfix" hack to allow
floated objects to add to the height of a div */
#infoBox:after {
visibility: hidden;
display: block;
font-size: 0;
content: " ";
clear: both;
height: 0;
}
div.infoTitle {
width: 100px;
font-weight: normal;
color: #787878;
float: left;
margin-left: 4px;
}
div.infoValues {
width: 30px;
text-align: right;
float: right;
}
label:hover, label:focus {
background: #CEDFF2;
}
</style>
<script src="../release/go.js"></script>
<script src="../extensions/Figures.js"></script>
<script src="../assets/js/goSamples.js"></script> <!-- this is only for the GoJS Samples framework -->
<script id="code">
var myDiagram;
var myLocation = { // this controls the data properties used by data binding conversions
x: "sepalLength",
y: "sepalWidth"
}
var lastStroked = null; // this remembers the last highlight Shape
function init() {
if (window.goSamples) goSamples(); // init for these samples -- you don't need to call this
var $ = go.GraphObject.make; // for conciseness in defining templates
var myToolTip = $(go.HTMLInfo, {
show: showToolTip,
// do nothing on hide: This tooltip doesn't hide unless the mouse leaves the diagram
})
myDiagram =
$(go.Diagram, "myDiagramDiv", // create a Diagram for the DIV HTML element
{
"animationManager.initialAnimationStyle": go.AnimationManager.AnimateLocations,
contentAlignment: go.Spot.Center, // content is always centered in the viewport
autoScale: go.Diagram.Uniform, // scale always has all content fitting in the viewport
"toolManager.hoverDelay": 10, // how quickly tooltips are shown
isReadOnly: true, // don't let users modify anything
mouseOver: doMouseOver, // this event handler is defined below
click: doMouseOver // this event handler is defined below
});
// define a simple Node template
myDiagram.nodeTemplate =
$(go.Node, "Auto",
{
selectable: false,
toolTip: myToolTip
},
new go.Binding("location", "", function(nothing, elt) {
return new go.Point(elt.data[myLocation.x] * 200,
elt.data[myLocation.y] * 200)
}),
new go.AnimationTrigger("position", null, go.AnimationTrigger.Bundled),
$(go.Shape, "Hexagon",
{
name: "SHAPE",
width: 20, height: 20,
strokeWidth: 4, stroke: null
},
new go.Binding("fill", "species", function(name) {
switch (name) {
case "Iris-setosa": return "rgba(240, 120, 50, .6)";
case "Iris-versicolor": return "rgba(240, 230, 120, .6)";
case "Iris-virginica": return "rgba(125, 200, 120, .6)";
}
return "black";
}))
);
// This is the fundamental data set, taken from:
// https://en.wikipedia.org/wiki/Iris_flower_data_set
var irisData = [
[5.1, 3.5, 1.4, 0.2, "Iris-setosa"],
[4.9, 3.0, 1.4, 0.2, "Iris-setosa"],
[4.7, 3.2, 1.3, 0.2, "Iris-setosa"],
[4.6, 3.1, 1.5, 0.2, "Iris-setosa"],
[5.0, 3.6, 1.4, 0.2, "Iris-setosa"],
[5.4, 3.9, 1.7, 0.4, "Iris-setosa"],
[4.6, 3.4, 1.4, 0.3, "Iris-setosa"],
[5.0, 3.4, 1.5, 0.2, "Iris-setosa"],
[4.4, 2.9, 1.4, 0.2, "Iris-setosa"],
[4.9, 3.1, 1.5, 0.1, "Iris-setosa"],
[5.4, 3.7, 1.5, 0.2, "Iris-setosa"],
[4.8, 3.4, 1.6, 0.2, "Iris-setosa"],
[4.8, 3.0, 1.4, 0.1, "Iris-setosa"],
[4.3, 3.0, 1.1, 0.1, "Iris-setosa"],
[5.8, 4.0, 1.2, 0.2, "Iris-setosa"],
[5.7, 4.4, 1.5, 0.4, "Iris-setosa"],
[5.4, 3.9, 1.3, 0.4, "Iris-setosa"],
[5.1, 3.5, 1.4, 0.3, "Iris-setosa"],
[5.7, 3.8, 1.7, 0.3, "Iris-setosa"],
[5.1, 3.8, 1.5, 0.3, "Iris-setosa"],
[5.4, 3.4, 1.7, 0.2, "Iris-setosa"],
[5.1, 3.7, 1.5, 0.4, "Iris-setosa"],
[4.6, 3.6, 1.0, 0.2, "Iris-setosa"],
[5.1, 3.3, 1.7, 0.5, "Iris-setosa"],
[4.8, 3.4, 1.9, 0.2, "Iris-setosa"],
[5.0, 3.0, 1.6, 0.2, "Iris-setosa"],
[5.0, 3.4, 1.6, 0.4, "Iris-setosa"],
[5.2, 3.5, 1.5, 0.2, "Iris-setosa"],
[5.2, 3.4, 1.4, 0.2, "Iris-setosa"],
[4.7, 3.2, 1.6, 0.2, "Iris-setosa"],
[4.8, 3.1, 1.6, 0.2, "Iris-setosa"],
[5.4, 3.4, 1.5, 0.4, "Iris-setosa"],
[5.2, 4.1, 1.5, 0.1, "Iris-setosa"],
[5.5, 4.2, 1.4, 0.2, "Iris-setosa"],
[4.9, 3.1, 1.5, 0.2, "Iris-setosa"],
[5.0, 3.2, 1.2, 0.2, "Iris-setosa"],
[5.5, 3.5, 1.3, 0.2, "Iris-setosa"],
[4.9, 3.6, 1.4, 0.1, "Iris-setosa"],
[4.4, 3.0, 1.3, 0.2, "Iris-setosa"],
[5.1, 3.4, 1.5, 0.2, "Iris-setosa"],
[5.0, 3.5, 1.3, 0.3, "Iris-setosa"],
[4.5, 2.3, 1.3, 0.3, "Iris-setosa"],
[4.4, 3.2, 1.3, 0.2, "Iris-setosa"],
[5.0, 3.5, 1.6, 0.6, "Iris-setosa"],
[5.1, 3.8, 1.9, 0.4, "Iris-setosa"],
[4.8, 3.0, 1.4, 0.3, "Iris-setosa"],
[5.1, 3.8, 1.6, 0.2, "Iris-setosa"],
[4.6, 3.2, 1.4, 0.2, "Iris-setosa"],
[5.3, 3.7, 1.5, 0.2, "Iris-setosa"],
[5.0, 3.3, 1.4, 0.2, "Iris-setosa"],
[7.0, 3.2, 4.7, 1.4, "Iris-versicolor"],
[6.4, 3.2, 4.5, 1.5, "Iris-versicolor"],
[6.9, 3.1, 4.9, 1.5, "Iris-versicolor"],
[5.5, 2.3, 4.0, 1.3, "Iris-versicolor"],
[6.5, 2.8, 4.6, 1.5, "Iris-versicolor"],
[5.7, 2.8, 4.5, 1.3, "Iris-versicolor"],
[6.3, 3.3, 4.7, 1.6, "Iris-versicolor"],
[4.9, 2.4, 3.3, 1.0, "Iris-versicolor"],
[6.6, 2.9, 4.6, 1.3, "Iris-versicolor"],
[5.2, 2.7, 3.9, 1.4, "Iris-versicolor"],
[5.0, 2.0, 3.5, 1.0, "Iris-versicolor"],
[5.9, 3.0, 4.2, 1.5, "Iris-versicolor"],
[6.0, 2.2, 4.0, 1.0, "Iris-versicolor"],
[6.1, 2.9, 4.7, 1.4, "Iris-versicolor"],
[5.6, 2.9, 3.6, 1.3, "Iris-versicolor"],
[6.7, 3.1, 4.4, 1.4, "Iris-versicolor"],
[5.6, 3.0, 4.5, 1.5, "Iris-versicolor"],
[5.8, 2.7, 4.1, 1.0, "Iris-versicolor"],
[6.2, 2.2, 4.5, 1.5, "Iris-versicolor"],
[5.6, 2.5, 3.9, 1.1, "Iris-versicolor"],
[5.9, 3.2, 4.8, 1.8, "Iris-versicolor"],
[6.1, 2.8, 4.0, 1.3, "Iris-versicolor"],
[6.3, 2.5, 4.9, 1.5, "Iris-versicolor"],
[6.1, 2.8, 4.7, 1.2, "Iris-versicolor"],
[6.4, 2.9, 4.3, 1.3, "Iris-versicolor"],
[6.6, 3.0, 4.4, 1.4, "Iris-versicolor"],
[6.8, 2.8, 4.8, 1.4, "Iris-versicolor"],
[6.7, 3.0, 5.0, 1.7, "Iris-versicolor"],
[6.0, 2.9, 4.5, 1.5, "Iris-versicolor"],
[5.7, 2.6, 3.5, 1.0, "Iris-versicolor"],
[5.5, 2.4, 3.8, 1.1, "Iris-versicolor"],
[5.5, 2.4, 3.7, 1.0, "Iris-versicolor"],
[5.8, 2.7, 3.9, 1.2, "Iris-versicolor"],
[6.0, 2.7, 5.1, 1.6, "Iris-versicolor"],
[5.4, 3.0, 4.5, 1.5, "Iris-versicolor"],
[6.0, 3.4, 4.5, 1.6, "Iris-versicolor"],
[6.7, 3.1, 4.7, 1.5, "Iris-versicolor"],
[6.3, 2.3, 4.4, 1.3, "Iris-versicolor"],
[5.6, 3.0, 4.1, 1.3, "Iris-versicolor"],
[5.5, 2.5, 4.0, 1.3, "Iris-versicolor"],
[5.5, 2.6, 4.4, 1.2, "Iris-versicolor"],
[6.1, 3.0, 4.6, 1.4, "Iris-versicolor"],
[5.8, 2.6, 4.0, 1.2, "Iris-versicolor"],
[5.0, 2.3, 3.3, 1.0, "Iris-versicolor"],
[5.6, 2.7, 4.2, 1.3, "Iris-versicolor"],
[5.7, 3.0, 4.2, 1.2, "Iris-versicolor"],
[5.7, 2.9, 4.2, 1.3, "Iris-versicolor"],
[6.2, 2.9, 4.3, 1.3, "Iris-versicolor"],
[5.1, 2.5, 3.0, 1.1, "Iris-versicolor"],
[5.7, 2.8, 4.1, 1.3, "Iris-versicolor"],
[6.3, 3.3, 6.0, 2.5, "Iris-virginica"],
[5.8, 2.7, 5.1, 1.9, "Iris-virginica"],
[7.1, 3.0, 5.9, 2.1, "Iris-virginica"],
[6.3, 2.9, 5.6, 1.8, "Iris-virginica"],
[6.5, 3.0, 5.8, 2.2, "Iris-virginica"],
[7.6, 3.0, 6.6, 2.1, "Iris-virginica"],
[4.9, 2.5, 4.5, 1.7, "Iris-virginica"],
[7.3, 2.9, 6.3, 1.8, "Iris-virginica"],
[6.7, 2.5, 5.8, 1.8, "Iris-virginica"],
[7.2, 3.6, 6.1, 2.5, "Iris-virginica"],
[6.5, 3.2, 5.1, 2.0, "Iris-virginica"],
[6.4, 2.7, 5.3, 1.9, "Iris-virginica"],
[6.8, 3.0, 5.5, 2.1, "Iris-virginica"],
[5.7, 2.5, 5.0, 2.0, "Iris-virginica"],
[5.8, 2.8, 5.1, 2.4, "Iris-virginica"],
[6.4, 3.2, 5.3, 2.3, "Iris-virginica"],
[6.5, 3.0, 5.5, 1.8, "Iris-virginica"],
[7.7, 3.8, 6.7, 2.2, "Iris-virginica"],
[7.7, 2.6, 6.9, 2.3, "Iris-virginica"],
[6.0, 2.2, 5.0, 1.5, "Iris-virginica"],
[6.9, 3.2, 5.7, 2.3, "Iris-virginica"],
[5.6, 2.8, 4.9, 2.0, "Iris-virginica"],
[7.7, 2.8, 6.7, 2.0, "Iris-virginica"],
[6.3, 2.7, 4.9, 1.8, "Iris-virginica"],
[6.7, 3.3, 5.7, 2.1, "Iris-virginica"],
[7.2, 3.2, 6.0, 1.8, "Iris-virginica"],
[6.2, 2.8, 4.8, 1.8, "Iris-virginica"],
[6.1, 3.0, 4.9, 1.8, "Iris-virginica"],
[6.4, 2.8, 5.6, 2.1, "Iris-virginica"],
[7.2, 3.0, 5.8, 1.6, "Iris-virginica"],
[7.4, 2.8, 6.1, 1.9, "Iris-virginica"],
[7.9, 3.8, 6.4, 2.0, "Iris-virginica"],
[6.4, 2.8, 5.6, 2.2, "Iris-virginica"],
[6.3, 2.8, 5.1, 1.5, "Iris-virginica"],
[6.1, 2.6, 5.6, 1.4, "Iris-virginica"],
[7.7, 3.0, 6.1, 2.3, "Iris-virginica"],
[6.3, 3.4, 5.6, 2.4, "Iris-virginica"],
[6.4, 3.1, 5.5, 1.8, "Iris-virginica"],
[6.0, 3.0, 4.8, 1.8, "Iris-virginica"],
[6.9, 3.1, 5.4, 2.1, "Iris-virginica"],
[6.7, 3.1, 5.6, 2.4, "Iris-virginica"],
[6.9, 3.1, 5.1, 2.3, "Iris-virginica"],
[5.8, 2.7, 5.1, 1.9, "Iris-virginica"],
[6.8, 3.2, 5.9, 2.3, "Iris-virginica"],
[6.7, 3.3, 5.7, 2.5, "Iris-virginica"],
[6.7, 3.0, 5.2, 2.3, "Iris-virginica"],
[6.3, 2.5, 5.0, 1.9, "Iris-virginica"],
[6.5, 3.0, 5.2, 2.0, "Iris-virginica"],
[6.2, 3.4, 5.4, 2.3, "Iris-virginica"],
[5.9, 3.0, 5.1, 1.8, "Iris-virginica"]
];
// now convert that data into an Array of JavaScript Objects,
// to be used as the Model.nodeDataArray
var array = [];
for (var i = 0; i < irisData.length; i++) {
var line = irisData[i];
var data = {
sepalLength: line[0],
sepalWidth: line[1],
petalLength: line[2],
petalWidth: line[3],
species: line[4]
};
array.push(data);
}
// create the Model for the Diagram to display
myDiagram.model = new go.Model(array);
// Called when the mouse is over the diagram's background
function doMouseOver(e) {
if (e === undefined) e = myDiagram.lastInput;
var doc = e.documentPoint;
// find all Nodes that are within 100 units
var list = myDiagram.findObjectsNear(doc, 100, null, function(x) { return x instanceof go.Node; });
// now find the one that is closest to e.documentPoint
var closest = null;
var closestDist = 999999999;
list.each(function(node) {
var dist = doc.distanceSquaredPoint(node.getDocumentPoint(go.Spot.Center));
if (dist < closestDist) {
closestDist = dist;
closest = node;
}
});
showToolTip(closest, myDiagram);
}
// Called with a Node (or null) that the mouse is over or near
function showToolTip(obj, diagram) {
if (obj !== null) {
var node = obj.part;
var e = diagram.lastInput;
var shape = node.findObject("SHAPE");
shape.stroke = "white";
if (lastStroked !== null && lastStroked !== shape) lastStroked.stroke = null;
lastStroked = shape;
updateInfoBox(e.viewPoint, node.data);
} else {
if (lastStroked !== null) lastStroked.stroke = null;
lastStroked = null;
document.getElementById("infoBoxHolder").innerHTML = "";
}
}
// Make sure the infoBox is momentarily hidden if the user tries to mouse over it
var infoBoxH = document.getElementById("infoBoxHolder");
infoBoxH.addEventListener("mousemove", function() {
var box = document.getElementById("infoBoxHolder");
box.style.left = parseInt(box.style.left) + "px";
box.style.top = parseInt(box.style.top) + 30 + "px";
}, false);
var diagramDiv = document.getElementById("myDiagramDiv");
// Make sure the infoBox is hidden when the mouse is not over the Diagram
diagramDiv.addEventListener("mouseout", function(e) {
if (lastStroked !== null) lastStroked.stroke = null;
lastStroked = null;
var infoBox = document.getElementById("infoBox");
var elem = document.elementFromPoint(e.clientX, e.clientY);
if (elem !== null && (elem === infoBox || elem.parentNode === infoBox)) {
var box = document.getElementById("infoBoxHolder");
box.style.left = parseInt(box.style.left) + "px";
box.style.top = parseInt(box.style.top) + 30 + "px";
} else {
var box = document.getElementById("infoBoxHolder");
box.innerHTML = "";
}
}, false);
} // end init
// This function is called to update the tooltip information
// depending on the bound data of the Node that is closest to the pointer.
function updateInfoBox(mousePt, data) {
var box = document.getElementById("infoBoxHolder");
box.innerHTML = "";
var infobox = document.createElement("div");
infobox.id = "infoBox";
box.appendChild(infobox);
for (var i = 0; i < 9; i++) {
var child = document.createElement("div");
switch (i) {
case 0: child.textContent = data.species; break;
case 1: child.className = "infoTitle"; child.textContent = "Sepal Length"; break;
case 2: child.className = "infoValues"; child.textContent = data.sepalLength; break;
case 3: child.className = "infoTitle"; child.textContent = "Sepal Width"; break;
case 4: child.className = "infoValues"; child.textContent = data.sepalWidth; break;
case 5: child.className = "infoTitle"; child.textContent = "Petal Length"; break;
case 6: child.className = "infoValues"; child.textContent = data.petalLength; break;
case 7: child.className = "infoTitle"; child.textContent = "Petal Width"; break;
case 8: child.className = "infoValues"; child.textContent = data.petalWidth; break;
}
infobox.appendChild(child);
}
box.style.left = mousePt.x + 30 + "px";
box.style.top = mousePt.y + 20 + "px";
}
// This function is called which a radio button is pressed.
// It changes the value of a variable that the node template's location conversion function uses.
// It then updates all target bindings, causing all node locations to change.
function changeAxes(e) {
var value = e.value;
if (e.name === "X") {
myLocation.x = value;
} else {
myLocation.y = value;
}
myDiagram.startTransaction("updateBindings");
myDiagram.updateAllTargetBindings();
myDiagram.commitTransaction("updateBindings");
}
</script>
</head>
<body onload="init()">
<div id="sample">
<div style="display: inline-block">
<!--The DIV for the Diagram needs an explicit size or else we won't see anything.
Also add a border to help see the edges. -->
<div id="myDiagramDiv" style="background-color: #3D3D3D; border: solid 1px black; width:500px; height:500px"></div>
<!-- This sibling of the Diagram provides information when the mouse is near a Diagram Node -->
<div id="infoBoxHolder">
<!-- Initially Empty, it is populated when updateInfoBox is called -->
</div>
<div id="controls">
<div style="border: solid 1px gray; float: left; padding: 2px;">
<p style="text-align: center;">X-axis</p>
<hr/>
<input type="radio" name="X" onclick="changeAxes(this)" value="sepalLength" id="SLx" checked/>
<label for="SLx">Sepal Length</label><br/>
<input type="radio" name="X" onclick="changeAxes(this)" value="sepalWidth" id="SWx" />
<label for="SWx">Sepal Width</label><br/>
<input type="radio" name="X" onclick="changeAxes(this)" value="petalLength" id="PLx"/>
<label for="PLx">Petal Length</label><br/>
<input type="radio" name="X" onclick="changeAxes(this)" value="petalWidth" id="PWx"/>
<label for="PWx">Petal Width</label><br/>
</div>
<div style="border: solid 1px gray; float: left; margin-left: 10px; padding: 2px;">
<p style="text-align: center;">Y-axis</p>
<hr/>
<input type="radio" name="Y" onclick="changeAxes(this)" value="sepalLength" id="SLy"/>
<label for="SLy">Sepal Length</label><br/>
<input type="radio" name="Y" onclick="changeAxes(this)" value="sepalWidth" id="SWy" checked/>
<label for="SWy">Sepal Width</label><br/>
<input type="radio" name="Y" onclick="changeAxes(this)" value="petalLength" id="PLy"/>
<label for="PLy">Petal Length</label><br/>
<input type="radio" name="Y" onclick="changeAxes(this)" value="petalWidth" id="PWy"/>
<label for="PWy">Petal Width</label><br/>
</div>
<div id="description" style="float: left;">
<p>This sample gives an example of a Diagram interacting with other HTML elements on the page.</p>
<p>As the mouse moves over the diagram, a formatted HTML DIV element displays information about the nearest Node.</p>
<p>The data displayed is from the <a href="https://en.wikipedia.org/wiki/Iris_flower_data_set">Iris flower data set</a>,
describing the variations in dimensions of three related flower species.</p>
</div>
</div>
</div>
</div>
</body>
</html>