UNPKG

estimate-error-bar

Version:

Simple error bar with qualitative range bands

181 lines (160 loc) 5.31 kB
var EstimateErrorBar = function(d3) { var _svgContainer; function create(data, options) { var labelWidth = options.maxWidth * 0.1; var labelHeight = options.maxHeight * 0.5; var barWidth = options.maxWidth - labelWidth * 2; var barHeight = options.maxHeight - labelHeight; var greenMax = options.greenMax; var yellowMax = options.yellowMax; var redMax = options.redMax; // find our max (better handle max > 10 just in case) if (+data["percentile-90"] > redMax) { redMax = Math.ceil(+data["percentile-90"] * 1.1); } // setup the SVG container _svgContainer = d3 .select("#" + options.containerId) .append("svg") .attr("width", options.maxWidth) .attr("height", options.maxHeight); // create our scale var linearScale = d3 .scaleLinear() .domain([0, redMax]) // set data domain .range([labelWidth, barWidth]); // and fit to pixel range // labels var minLabel = _svgContainer .append("text") .attr("x", labelWidth / 2) .attr("y", barHeight * 0.666) .attr("font-family", options.fontFamily) .attr("font-size", options.fontSize) .style("text-anchor", "middle") .text(0); var maxLabel = _svgContainer .append("text") .attr("x", barWidth + labelWidth / 2) .attr("y", barHeight * 0.666) .attr("width", labelWidth) .attr("font-family", options.fontFamily) .style("text-anchor", "middle") .attr("font-size", options.fontSize) .text(redMax); var unitsLabel = data.units + " / " + data["context-units"]; var axisLabel = _svgContainer .append("text") .attr("x", (barWidth + labelWidth) / 2) .attr("y", barHeight + labelHeight) .attr("width", labelWidth) .attr("font-family", options.fontFamily) .attr("font-size", options.fontSize) .style("text-anchor", "middle") .text(unitsLabel); // coloured rectangles var green = _svgContainer .append("rect") .attr("x", labelWidth) .attr("y", 0) .attr("fill", "#7ec891") .attr("width", linearScale(greenMax)) .attr("height", barHeight); var yellow = _svgContainer .append("rect") .attr("x", linearScale(greenMax)) .attr("y", 0) .attr("fill", "#f5c85b") .attr("width", linearScale(yellowMax) - linearScale(greenMax)) .attr("height", barHeight); var red = _svgContainer .append("rect") .attr("x", linearScale(yellowMax)) .attr("y", 0) .attr("fill", "#fe5e69") .attr("width", linearScale(redMax) - linearScale(yellowMax)) .attr("height", barHeight); // min/max lines, ad connect them var scaledMin = linearScale(+data["percentile-10"]); var minLine = _svgContainer .append("line") .attr("x1", scaledMin) .attr("y1", 5) .attr("x2", scaledMin) .attr("y2", barHeight - 5) .attr("stroke-width", 2) .attr("stroke", "#444"); var scaledMax = linearScale(+data["percentile-90"]); var maxLine = _svgContainer .append("line") .attr("x1", scaledMax) .attr("y1", 5) .attr("x2", scaledMax) .attr("y2", barHeight - 5) .attr("stroke-width", 2) .attr("stroke", "#444"); var connectingLine = _svgContainer .append("line") .attr("x1", scaledMin) .attr("y1", barHeight / 2) .attr("x2", scaledMax) .attr("y2", barHeight / 2) .attr("stroke-width", 2) .attr("stroke", "#444"); // median circle needs to go in a block so we can center text var scaledMedian = linearScale(+data["median"]); var medianBlock = _svgContainer.append("g").attr("transform", function(d) { return "translate(" + scaledMedian + "," + barHeight / 2 + ")"; }); // create the circle var circleRadius = barHeight / 3; var circle = medianBlock .append("circle") .attr("r", circleRadius) .attr("fill", "#444"); // create text for the block medianBlock .append("text") .attr("dx", function(d) { return -options.fontSize / 4; }) .attr("dy", function(d) { return options.fontSize / 3; }) .attr("font-size", options.fontSize) .style("fill", "#FFF") .text(parseInt(data["median"])); // tooltip var tooltipText = "Median estimate is " + data["median"] + " " + unitsLabel; var tooltip = d3 .select("body") .append("div") .style("position", "absolute") .style("z-index", "99999") .style("visibility", "hidden") .attr("class", "estimate-bar-tooltip") .text(tooltipText); _svgContainer .on("mouseover", function() { return tooltip.style("visibility", "visible"); }) .on("mousemove", function() { return tooltip .style("top", parseInt(event.pageY - 50) + "px") .style("left", parseInt(event.pageX + 10) + "px"); }) .on("mouseout", function() { return tooltip.style("visibility", "hidden"); }); } function destroy() { if (_svgContainer) { _svgContainer.selectAll("*").remove(); _svgContainer.remove(); } } return { create: create, destroy: destroy }; }; export default EstimateErrorBar;