Simplifying calculations, adding inverse of plot, working sensibly with D3 scales etc.
data reverse engineered from here... http://thegrowingseason.wordpress.com/2013/05/10/soils-management-the-old-jar-test/
| notional2005result.csv | |
| notional2005result.json |
Simplifying calculations, adding inverse of plot, working sensibly with D3 scales etc.
data reverse engineered from here... http://thegrowingseason.wordpress.com/2013/05/10/soils-management-the-old-jar-test/
| { | |
| "sand":[ | |
| { | |
| "clay":0, | |
| "sand":100, | |
| "silt":0 | |
| }, | |
| { | |
| "clay":10, | |
| "sand":90, | |
| "silt":0 | |
| }, | |
| { | |
| "clay":0, | |
| "sand":90, | |
| "silt":10 | |
| } | |
| ], | |
| "loamy sand":[ | |
| { | |
| "clay":0, | |
| "sand":90, | |
| "silt":10 | |
| }, | |
| { | |
| "clay":10, | |
| "sand":90, | |
| "silt":0 | |
| }, | |
| { | |
| "clay":15, | |
| "sand":85, | |
| "silt":0 | |
| }, | |
| { | |
| "clay":0, | |
| "sand":70, | |
| "silt":30 | |
| } | |
| ], | |
| "sandy loam":[ | |
| { | |
| "clay":0, | |
| "sand":70, | |
| "silt":30 | |
| }, | |
| { | |
| "clay":15, | |
| "sand":85, | |
| "silt":0 | |
| }, | |
| { | |
| "clay":20, | |
| "sand":80, | |
| "silt":0 | |
| }, | |
| { | |
| "clay":20, | |
| "sand":53, | |
| "silt":32 | |
| }, | |
| { | |
| "clay":5, | |
| "sand":53, | |
| "silt":42 | |
| }, | |
| { | |
| "clay":5, | |
| "sand":45, | |
| "silt":50 | |
| }, | |
| { | |
| "clay":0, | |
| "sand":50, | |
| "silt":50 | |
| } | |
| ], | |
| "sandy clay loam":[ | |
| { | |
| "clay":20, | |
| "sand":80, | |
| "silt":0 | |
| }, | |
| { | |
| "clay":35, | |
| "sand":65, | |
| "silt":0 | |
| }, | |
| { | |
| "clay":35, | |
| "sand":45, | |
| "silt":20 | |
| }, | |
| { | |
| "clay":28, | |
| "sand":45, | |
| "silt":27 | |
| }, | |
| { | |
| "clay":20, | |
| "sand":53, | |
| "silt":32 | |
| } | |
| ], | |
| "sandy clay":[ | |
| { | |
| "clay":35, | |
| "sand":65, | |
| "silt":0 | |
| }, | |
| { | |
| "clay":35, | |
| "sand":45, | |
| "silt":20 | |
| }, | |
| { | |
| "clay":55, | |
| "sand":45, | |
| "silt":0 | |
| }], | |
| "clay":[ | |
| { | |
| "clay":55, | |
| "sand":45, | |
| "silt":0 | |
| }, | |
| { | |
| "clay":100, | |
| "sand":0, | |
| "silt":0 | |
| }, | |
| { | |
| "clay":60, | |
| "sand":0, | |
| "silt":40 | |
| }, | |
| { | |
| "clay":40, | |
| "sand":20, | |
| "silt":40 | |
| }, | |
| { | |
| "clay":40, | |
| "sand":45, | |
| "silt":15 | |
| } | |
| ], | |
| "clay loam":[ | |
| { | |
| "clay":40, | |
| "sand":45, | |
| "silt":15 | |
| }, | |
| { | |
| "clay":40, | |
| "sand":20, | |
| "silt":40 | |
| }, | |
| { | |
| "clay":28, | |
| "sand":20, | |
| "silt":52 | |
| }, | |
| { | |
| "clay":28, | |
| "sand":45, | |
| "silt":27 | |
| } | |
| ], | |
| "silty clay":[ | |
| { | |
| "clay":60, | |
| "sand":0, | |
| "silt":40 | |
| }, | |
| { | |
| "clay":40, | |
| "sand":0, | |
| "silt":60 | |
| }, | |
| { | |
| "clay":40, | |
| "sand":20, | |
| "silt":40 | |
| } | |
| ], | |
| "silty clay loam":[ | |
| { | |
| "clay":28, | |
| "sand":0, | |
| "silt":72 | |
| }, | |
| { | |
| "clay":28, | |
| "sand":20, | |
| "silt":52 | |
| }, | |
| { | |
| "clay":40, | |
| "sand":20, | |
| "silt":40 | |
| }, | |
| { | |
| "clay":40, | |
| "sand":0, | |
| "silt":60 | |
| } | |
| ], | |
| "silty loam":[ | |
| { | |
| "clay":0, | |
| "sand":50, | |
| "silt":50 | |
| }, | |
| { | |
| "clay":28, | |
| "sand":22, | |
| "silt":50 | |
| }, | |
| { | |
| "clay":28, | |
| "sand":0, | |
| "silt":72 | |
| }, | |
| { | |
| "clay":12, | |
| "sand":0, | |
| "silt":88 | |
| }, | |
| { | |
| "clay":12, | |
| "sand":8, | |
| "silt":80 | |
| }, | |
| { | |
| "clay":0, | |
| "sand":20, | |
| "silt":80 | |
| } | |
| ], | |
| "silt":[ | |
| { | |
| "clay":0, | |
| "sand":0, | |
| "silt":100 | |
| }, | |
| { | |
| "clay":0, | |
| "sand":20, | |
| "silt":80 | |
| }, | |
| { | |
| "clay":12, | |
| "sand":8, | |
| "silt":80 | |
| }, | |
| { | |
| "clay":12, | |
| "sand":0, | |
| "silt":88 | |
| } | |
| ], | |
| "loam":[ | |
| { | |
| "clay":28, | |
| "sand":45, | |
| "silt":27 | |
| }, | |
| { | |
| "clay":28, | |
| "sand":22, | |
| "silt":50 | |
| }, | |
| { | |
| "clay":5, | |
| "sand":45, | |
| "silt":50 | |
| }, | |
| { | |
| "clay":5, | |
| "sand":53, | |
| "silt":42 | |
| }, | |
| { | |
| "clay":20, | |
| "sand":53, | |
| "silt":32 | |
| } | |
| ] | |
| } |
| <html> | |
| <head> | |
| <title>ternary 2</title> | |
| <script charset="UTF-8" src="http://cdnjs.cloudflare.com/ajax/libs/d3/3.1.6/d3.min.js"></script> | |
| <style type="text/css"> | |
| .ternary-circle{ | |
| stroke:#c00; | |
| fill:#fff; | |
| } | |
| .ternary-line{ | |
| fill:none; | |
| stroke:#c00; | |
| } | |
| .ternary-tick{ | |
| fill:none; | |
| stroke:#aaa; | |
| } | |
| .minor{ | |
| stroke:#fefefe; | |
| } | |
| </style> | |
| </head> | |
| <body> | |
| </body> | |
| <script type="text/javascript"> | |
| var test_data = [ | |
| {a:100,b:0,c:0,color:'#F00'}, | |
| {a:0,b:100,c:0,color:'#0F0'}, | |
| {a:0,b:0,c:100,color:'#00F'}, | |
| {a:33,b:33,c:33,color:'#999'} | |
| ]; | |
| function ternaryPlot(){ | |
| var ternary = {}; | |
| var height = Math.sqrt( 1*1 - (1/2)*(1/2)); | |
| var path; | |
| function rescale(range){ | |
| if(!range.length) range = [0, 1]; | |
| ternary.scale = d3.scale.linear().domain([0, 1]).range(range); | |
| } | |
| function line(interpolator){ | |
| if(!interpolator) interpolator = 'linear' | |
| path = d3.svg.line() | |
| .x(function(d) { return d[0]; }) | |
| .y(function(d) { return d[1]; }) | |
| .interpolate(interpolator); | |
| } | |
| rescale([0, 400]); | |
| line(); | |
| ternary.range = function(range){ | |
| rescale(range); | |
| return ternary; | |
| }; | |
| //map teranry coordinate [a, b, c] to an [x, y] position | |
| ternary.point = function(coords){ | |
| var pos = [0,0]; | |
| var sum = d3.sum(coords); | |
| if(sum !== 0) { | |
| var normalized = coords.map( function(d){ return d/sum; } ); | |
| pos[0] = ternary.scale ( normalized[1] + normalized[2] / 2 ); | |
| pos[1] = ternary.scale ( height * normalized[0] + height * normalized[1] ); | |
| } | |
| return pos; | |
| }; | |
| //create an SVG path from a set of points | |
| ternary.line = function(coordsList, accessor, interpolator){ //path generator wrapper | |
| if(interpolator) line(interpolator) | |
| if(!accessor) accessor = function(d){ return d; } | |
| var positions = coordsList.map( function(d){ | |
| return ternary.point( accessor(d) ); | |
| }); | |
| return path(positions); | |
| }; | |
| ternary.rule = function(value, axis){ | |
| console.log(value, axis); | |
| var ends = []; | |
| if(axis == 0){ | |
| ends = [ | |
| [value, 0, 100-value], | |
| [value, 100-value, 0] | |
| ]; | |
| }else if(axis == 1){ | |
| ends = [ | |
| [0, value, 100-value], | |
| [100-value, value, 0] | |
| ]; | |
| }else if(axis == 2){ | |
| ends = [ | |
| [0, 100-value, value], | |
| [100-value, 0, value] | |
| ]; | |
| } | |
| return ternary.line(ends); | |
| } | |
| // this inverse of point i.e. take an x,y positon and get the ternary coordinate | |
| ternary.getValues = function(pos){ //NOTE! haven't checked if this works yet | |
| pos = pos.map(ternary.scale.inverse); | |
| var c = 1 - pos[1]; | |
| var b = pos[0] - c/2; | |
| var a = y - b; | |
| return [a, b, c]; | |
| }; | |
| return ternary; | |
| } | |
| //// | |
| function ternaryAxes(plot){ | |
| var axes = {}; | |
| var parent = d3.select('svg'); | |
| var defaultTicks = d3.range(0,101,25); | |
| var ticks = [defaultTicks, defaultTicks, defaultTicks]; | |
| var minorTicks = [[],[],[]]; | |
| axes.draw = function(parentSelector){ | |
| if(parentSelector) parent = d3.select(parentSelector); | |
| var minor = parent.append('g').attr('id','minor-ticks'); | |
| var major = parent.append('g').attr('id','major-ticks') | |
| //minor ticks | |
| for (i = 0; i<minorTicks.length; i++){ | |
| for (j = 0; j<minorTicks[i].length; j++){ | |
| minor.append('path').attr({ | |
| 'class':'ternary-tick minor', | |
| 'd':plot.rule(minorTicks[i][j], i) | |
| }) | |
| } | |
| } | |
| //major ticks | |
| for (var i=0; i<ticks.length; i++){ | |
| for (var j=0; j<ticks[i].length; j++){ | |
| major.append('path').attr({ | |
| 'class':'ternary-tick', | |
| 'd':plot.rule(ticks[i][j], i) | |
| }) | |
| } | |
| } | |
| } | |
| axes.ticks = function(tickArrays){ // an array containing 1 - 3 three arrays the first array will be copied over empty spaces at the end | |
| if(!tickArrays) tickArrays = [defaultTicks,defaultTicks,defaultTicks]; | |
| if(!tickArrays[1]) tickArrays[1] = tickArrays[0]; | |
| if(!tickArrays[2]) tickArrays[2] = tickArrays[0]; | |
| ticks = tickArrays; | |
| return axes; | |
| } | |
| axes.minorTicks = function(tickArrays){ | |
| if(!tickArrays) tickArrays = [[],[],[]]; | |
| if(!tickArrays[1]) tickArrays[1] = tickArrays[0]; | |
| if(!tickArrays[2]) tickArrays[2] = tickArrays[0]; | |
| minorTicks = tickArrays; | |
| return axes; | |
| } | |
| return axes; | |
| } | |
| //// | |
| var svg = d3.select('body').append('svg').attr({ width:500, height:500 }); | |
| var axes = svg.append('g').attr('id','axes'); | |
| var plot = svg.append('g').attr('id','plot'); | |
| var myTernary = ternaryPlot().range([0,400]); | |
| /*plot.append('path').attr( | |
| { | |
| d:function(){ | |
| return myTernary.line(test_data, function(d){ return [d.a, d.b, d.c]; }, 'basis') | |
| }, | |
| 'class':'ternary-line' | |
| }); | |
| plot.selectAll('circle') | |
| .data(test_data) | |
| .enter() | |
| .append('circle') | |
| .attr({ | |
| class:'ternary-circle', | |
| r:4, | |
| transform:function(d){ | |
| var point = myTernary.point([d.a, d.b, d.c]); | |
| return ("translate(" + point[0] + ", " + point[1] + ")"); | |
| }, | |
| fill:function(d){ return d.color } | |
| }); | |
| */ | |
| var myAxes = ternaryAxes(myTernary); | |
| myAxes.ticks().minorTicks([d3.range(0,101,5)]).draw('#axes'); | |
| d3.json('data.json', gotData); | |
| function gotData(d){ | |
| for (var type in d){ | |
| var f = function (){ | |
| return type | |
| } | |
| plot.append('path').attr( | |
| { | |
| d:function(){ | |
| return myTernary.line(d[type], function(d){ return [d.sand, d.silt, d.clay]; }) + "Z"; | |
| }, | |
| 'class':'ternary-line', | |
| 'id':type.replace(' ','-') | |
| }) | |
| .on('click', function(d){ | |
| console.log(this.id); | |
| }); | |
| } | |
| } | |
| </script> | |
| </html> |