<head>
<!-- Load plotly.js into the DOM -->
<script src='https://cdn.plot.ly/plotly-3.0.0.min.js'></script>
<script src='https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.17/d3.min.js'></script>
</head>
<body>
<div id='myDiv'><!-- Plotly chart will be drawn inside this DIV --></div>
</body>
let url = 'https://gist.githubusercontent.com/davenquinn/988167471993bc2ece29/raw/f38d9cb3dd86e315e237fde5d65e185c39c931c2/data.json';
d3.json(url, function(err, rawData) {
if(err) throw err;
plot(rawData);
});
function plot(rawData) {
let data = Object.keys(rawData).map(function(k) {
let pts = rawData[k];
return {
type: 'scatterternary',
mode: 'lines',
name: k,
hoverinfo: 'none',
a: pts.map(function(d) { return d.clay }),
b: pts.map(function(d) { return d.sand }),
c: pts.map(function(d) { return d.silt }),
line: { color: 'gray' }
};
});
let layout = {
ternary: {
sum: 100,
aaxis: makeAxis('Clay', "green"),
baxis: makeAxis('Sand', "blue"),
caxis: makeAxis('Silt', "red")
},
showlegend: false,
hoverdistance: 500,
shapes: [{
type: "line",
xref: 'domain',
yref: 'domain',
x0: 0,
x1: 0,
y0: 0,
y1: 0,
line: {
color: 'green',
width: 1,
}
},{
type: "line",
xref: 'domain',
yref: 'domain',
x0: 0,
x1: 0,
y0: 0,
y1: 0,
line: {
color: 'blue',
width: 1,
}
},{
type: "line",
xref: 'domain',
yref: 'domain',
x0: 0,
x1: 0,
y0: 0,
y1: 0,
line: {
color: 'red',
width: 1,
}
}],
annotations: [{
xref: "domain",
yref: "domain",
xanchor: "right",
yanchor: "center",
ax: -6,
ay: 0,
arrowwidth: 3,
arrowcolor: "green",
borderpad: 5,
visible: false,
align: 'right',
bgcolor: 'green',
font: {
color: "white"
}
},{
xref: "domain",
yref: "domain",
xanchor: "middle",
yanchor: "top",
ax: 0,
ay: 6,
arrowwidth: 3,
arrowcolor: "blue",
borderpad: 5,
visible: false,
align: 'center',
bgcolor: 'blue',
font: {
color: "white"
}
},{
xref: "domain",
yref: "domain",
xanchor: "left",
yanchor: "bottom",
ax: 0,
ay: 0,
arrowwidth: 0,
arrowcolor: "red",
borderpad: 5,
visible: false,
align: 'left',
bgcolor: 'red',
font: {
color: "white"
}
}]
}
let config = {
responsive: true
}
let layer = document.getElementById("myDiv")
Plotly.newPlot(layer, data, layout, config).then(_ => {
Plotly.relayout(layer, {autosize: true})
layer
.on("plotly_relayout", e => {
if(e.autosize) {
Plotly.update(layer, {}, {
margin: {
l: 0,
t: 0,
r: 0,
b: 0
}
})
const ternary = document.querySelector('.ternary .toplevel.plotbg')
const { height: th, width: tw } = ternary.getBBox()
const { height: lh, width: lw } = layer.getBoundingClientRect()
let mv = (lh - th) / 2
let mh = (lw - tw) / 2
mh += mv < 50 ? 58 : 0
mv += mv < 50 ? 50 : 0
mv += mh < 50 ? 50 : 0
mh += mh < 50 ? 58 : 0
Plotly.update(layer, {}, {
margin: {
l: mh,
t: mv,
r: mh,
b: mv
}
})
}
})
.on("plotly_click", e => {
const xaxi = e.points[0].xaxis
const left = layer._fullLayout.margin.l
const x = xaxi.r2fraction(xaxi.p2r(e.event.offsetX - left))
const yaxi = e.points[0].yaxis
const top = layer._fullLayout.margin.t
const y = yaxi.r2fraction(yaxi.p2r(e.event.offsetY - top))
const base = layer._fullLayout.ternary.sum
const { a, b, c } = xy2abc(x, y, base)
const { A, B, C } = xyEdges(a, b, c, base)
Plotly.relayout(layer, {
"annotations[0].x": A.x,
"annotations[0].y": A.y,
"annotations[0].text": `${parseInt(a)}%`,
"annotations[0].visible": true,
"annotations[1].x": B.x,
"annotations[1].y": B.y,
"annotations[1].text": `${parseInt(b)}%`,
"annotations[1].visible": true,
"annotations[2].x": C.x,
"annotations[2].y": C.y,
"annotations[2].text": `${parseInt(c)}%`,
"annotations[2].visible": true,
"shapes[0].x0": x,
"shapes[0].x1": A.x,
"shapes[0].y0": y,
"shapes[0].y1": A.y,
"shapes[1].x0": x,
"shapes[1].x1": B.x,
"shapes[1].y0": y,
"shapes[1].y1": B.y,
"shapes[2].x0": x,
"shapes[2].x1": C.x,
"shapes[2].y0": y,
"shapes[2].y1": C.y
})
})
})
}
function makeAxis(text, color) {
return {
title: {
text
},
color,
ticksuffix: '%',
min: 0.01,
linewidth: 2,
ticks: 'outside',
ticklen: 8,
showgrid: true,
}
}
function xy2abc(x, y, base) {
let a = base * y
let b = base * (1 - x - y / 2)
let c = base - a - b
return { a, b, c }
}
function xyEdges(a, b, c, base) {
let A = {
x: a / 2 / base,
y: a / base
}
let B = {
x: (base - b) / base,
y: 0
}
let C = {
x: (c + (a + b) / 2) / base,
y: (base - c) / base
}
return { A, B, C }
}
This Pen doesn't use any external CSS resources.
This Pen doesn't use any external JavaScript resources.