Built with blockbuilder.org
forked from RobertDelgado's block: Refactor Project with pan & zoom
xxxxxxxxxx
<head>
<meta charset="utf-8">
<script src="https://d3js.org/d3.v4.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/d3-tip/0.6.3/d3-tip.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/d3-legend/1.1.0/d3-legend.js"></script>
<style>
body { margin:0;position:fixed;top:0;right:0;bottom:0;left:0; }
.topMarks {}
.bottomMarks {}
.bottomMarks:hover{stroke:gray; stroke-width:.15em;}
.connectingMarks {}
.annotation{}
.xAxis, .yAxis {}
.floatingLabel {}
.graphContainer {}
.textMarker{}
.tooltip{
position: absolute;
text-align: center;
width: 60px;
height: 28px;
padding: 2px;
font: 12px sans-serif;
background: lightsteelblue;
border: 0px;
border-radius: 8px;
pointer-events: none;
}
</style>
</head>
<body>
<script>
// Feel free to change or delete any of the code you see in this editor!
var height = 500;
var width = 960;
var margin = {top: 30, bottom: 100, left:50, right:20};
var innerHeight = height - margin['top'] - margin['bottom'];
var innerWidth = height - margin['left'] - margin['right'];
function generalUpdate(obj, elt, data, attributes)
{
var className = ((elt.hasOwnProperty('cl'))? elt.class: elt.obj);
var output = obj.selectAll(className)
.data(data);
return(output)
};
function generalEnter(obj, elt, attributes)
{
var output = obj.enter().append(elt.obj);
if(elt.hasOwnProperty('class'))
output = output.attr('class', elt.class);
return(output)
};
function attributes(obj, attributes)
{
var output = obj;
for(var name in attributes)
{
if( attributes.hasOwnProperty(name))
{
output = output.attr(name, attributes[name]);
};
};
return(output);
}
//for zooming
function resetted() {
svg.transition()
.duration(750)
.call(zoom.transform, d3.zoomIdentity);
}
function render(data, year1)
{
var numberOfCountries = 10;
data = data.sort(function(x,y){return(x.Final_Rank - y.Final_Rank);});
data = data.filter(function(d){return d.Final_Rank <= numberOfCountries;});
var xValue = d => d.Country;//function(d){return d.Country;}
var xScale = d3.scaleBand().padding(.90);
xScale.domain(data.map(xValue))
.range([0,innerWidth]);
var yScale = d3.scaleLinear().domain([0,d3.max(data, d => d.GHG_Cum) + 1000])
.range([innerHeight,0]);
data = data.filter(function(d){return d.Year == year1; });
//console.log(data);
var yValueBottom = d => d.GHG_Cum - d.GHG;
var yValueTop = d => d.GHG_Cum
var body = d3.select('body');
var svg = generalUpdate(body, {obj:'svg'}, [null], {height:height,width:width});
svg = attributes(svg,{height:height,width:width} );
svg = generalEnter(svg, {obj: 'svg'} ); //Why do I need this here and not after svg declaration?
svg = attributes(svg,{height:height,width:width} );
var floatingLabel = svg
.selectAll('.floatingLabel')
.data([null]);
floatingLabel = floatingLabel.enter()
.append('g')
.attr("class", "floatingLabel")
.style("display", "none");
floatingLabel.append("rect")
.attr("width", 60)
.attr("height", 20)
.attr("fill", "white")
.style("opacity", .5);
var floatingText = floatingLabel.append("text")
.attr("x", 30)
.attr("dy", "1.2em")
.style("text-anchor", "middle")
.attr("font-size", "12px")
.attr("font-weight", "bold");
var popMarker = floatingText.append('tspan');
var cumGHGMarker = floatingText.append('tspan');
var group = generalUpdate(svg, {obj:'g', cl: '.graphContainer'},[null]);
group = attributes(group, {height:innerHeight
, width: innerWidth
, transform: `translate(${margin['left']}, ${margin['top'] -60})` });
group = generalEnter(group, {obj: 'g'}, {height:innerHeight
, width: innerWidth
, transform: `translate(${margin['left']+20}, ${margin['top'] -60})` });
group = attributes(group, {height:innerHeight
, width: innerWidth
, transform: `translate(${margin['left'] + 20}, ${margin['top'] -10})` });
///For zooming
function zoomed() {
group.attr("transform", d3.event.transform);
//xAxisG.call(xAxis.scale(d3.event.transform.rescaleX(x)));
//yAxisG.call(yAxis.scale(d3.event.transform.rescaleY(y)));
}
///For zooming
var zoom = d3.zoom()
.scaleExtent([1, 40])
.translateExtent([[-100, 0], [width + 9000, height]])
.on("zoom", zoomed);
var annotation = generalUpdate(group, {cl: '.annotation', obj:'text'}, [null]);
annotation = generalEnter(annotation, {cl: '.annotation', obj:'text'});
annotation = annotation.text("GHG emissions for " + numberOfCountries +" most prolific emitters in " + year1);
annotation = attributes(annotation, {
stroke: 'black'
, transform: 'translate(100, 100)'
, "font-size": '1.5em'
, fill: 'white'}
);
var topMarks = generalUpdate(group, {cl: ".topMarks", obj: "circle"}, data);
topMarks = generalEnter(topMarks,{cl: ".topMarks", obj: "circle"});
topMarks = topMarks.merge(topMarks);
topMarks = attributes(topMarks,{cx: function(d){return xScale(xValue(d)) + xScale.bandwidth()/2;}
, cy: function(d){return yScale(yValueTop(d));}
, r: function(d){return Math.sqrt(height * d.Population/(1000000*600));}
, fill: 'rgba(255,123,50,.2)'
} );
///For interactivity. I may modularize this
topMarks = topMarks.on("mouseover", function() { floatingLabel.style("display", null); d3.select(this).attr('fill','orange')})
.on("mouseout", function() { floatingLabel.style("display", "none");
d3.select(this).attr('fill','rgba(255,123,50,.2)');
})
.on("mousemove", function(d) {
//console.log(d);
var thisX = d3.mouse(this)[0];
var thisY = d3.mouse(this)[1];
var yOffset = (thisY < 100 ? -5 : -50 )
console.log(yOffset, thisY, yScale(1000000000));
var xPosition = thisX + 200;
var yPosition = thisY + yOffset;
floatingLabel.attr("transform", "translate(" + xPosition + "," + yPosition + ")");
popMarker.text("Pop (M): " + d.Population/1000000).attr("x", "0").attr("dy", "1.2em");
cumGHGMarker.text("Cum Emiss: " + d.GHG_Cum).attr("x", "0").attr("dy", "1.2em");
});
var bandwidth = xScale.bandwidth();
var connectingMarks = generalUpdate(group, {cl: '.connectingMarks', obj: 'rect'}, data);
connectingMarks = generalEnter(connectingMarks, {cl: '.connectingMarks', obj: 'rect'} );
connetingMarks = connectingMarks.merge(connectingMarks);
connectingMarks = attributes(connectingMarks, {x:function(d){return xScale(xValue(d));}
,y:function(d){return yScale(yValueTop(d));}
, width: function(d){return xScale.bandwidth();}
, height: function(d){return innerHeight - yScale(yValueTop(d) - yValueBottom(d));}});
var bottomMarks = generalUpdate(group, {cl:'.bottomMarks', obj: 'line'}, data);
bottomMarks = generalEnter(bottomMarks, {cl:'.bottomMarks', obj: 'line'} );
bottomMarks = bottomMarks.merge(bottomMarks);
bottomMarks = attributes(bottomMarks, {
x1: function(d){return(xScale(xValue(d)) + bandwidth/2);}
, x2: function(d){return(xScale(xValue(d)) + bandwidth/2);}
, y1: function(d){return yScale(yValueBottom(d));}
, y2: function(d){return yScale(1500) ;}
, stroke: 'black'
, 'stroke-dasharray': '5,2'
});
bottomMarks = bottomMarks.append('svg:title')
.text(function(d){return "Country: " + d.Country
+ "\nCumulative GHG emissions: "
+ d.GHG_Cum; + " tonnes"});
var xAxisG = generalUpdate(group, {obj: 'g', cl: '.xAxis'}, [null]);
xAxisG = generalEnter(xAxisG, {obj: 'g', cl: '.xAxis'});
xAxisG = xAxisG.style('font-size', '7px').style('font', 'Times').merge(xAxisG);
xAxisG = attributes(xAxisG, {'transform':`translate(0, ${yScale(innerHeight)})`});
var xAxisLabel = xAxisG.selectAll('.textLabel').data([null]);
xAxisLabel.enter().append('text').attr('class', 'textLabel')
.text('Country')
.attr('stroke', 'black')
.attr('fill', 'black')
.attr('font-size', '3em')
.attr('transform', 'translate(200,30)')
var xAxis = d3.axisBottom()
.scale(xScale)
.ticks(5)
.tickSize(width/50 + ' em');
var yAxisG = generalUpdate(group, {obj: 'g', cl: '.yAxis'}, [null]);
yAxisG = generalEnter(yAxisG, {obj: 'g', cl: '.yAxis'});
yAxisG = yAxisG.style('font-size', '15px').style('font', 'Helvetica').merge(yAxisG);
yAxisG = attributes(yAxisG, {'transform':`translate(${xScale(0)},0)`});
var yAxisLabel = yAxisG.selectAll(".textLabel")
.data([null]);
yAxisLabel = yAxisLabel.enter().append('text')
.attr('transform', 'rotate(-90) translate(-100, -45)')
.attr('class', "textLabel")
.text('GHG emissions (tonnes)')
.attr('stroke', 'black')
.attr('fill', 'black')
.attr('font-size', '1.5em')
var yAxis = d3.axisLeft()
.scale(yScale)
.ticks(5)
.tickSize(width/100 + ' em')
.tickFormat(d3.format('.0s'))
;
xAxisG.call(xAxis);
//xAxisG.selectAll('text')
// .attr('transform', 'rotate(-45)')
yAxisG.call(yAxis);
svg.exit().remove()
group.exit().remove();
topMarks.exit().remove();
connectingMarks.exit().remove();
bottomMarks.exit().remove();
annotation.exit().remove();
svg.call(zoom);
}
var rows = function(d){
d.Country = d.Country;
d.Year = +d.Year;
d.GHG = +d.GHG;
d.GHG_Cum = +d.GHG_Cum;
d.Population = +d.Population;
d.Final_Rank = +d.Final_Rank;
return(d);};
d3.csv('Data3.csv', rows, function(array){return(render(array, 2012));})
</script>
</body>
https://d3js.org/d3.v4.min.js
https://cdnjs.cloudflare.com/ajax/libs/d3-tip/0.6.3/d3-tip.min.js
https://cdnjs.cloudflare.com/ajax/libs/d3-legend/1.1.0/d3-legend.js