diff --git a/Makefile b/Makefile index c978f8fa..bc9d297f 100644 --- a/Makefile +++ b/Makefile @@ -3,7 +3,6 @@ JS_FILES := src/header.js \ src/baseFunctions.js \ src/chart.js \ - src/vega.js \ src/colors.js \ src/component/barGrouped.js \ src/component/barStacked.js \ @@ -15,7 +14,7 @@ JS_FILES := src/header.js \ src/component/legend.js \ src/component/lineChart.js \ src/component/heatMap.js \ - src/component/heatCircle.js \ + src/component/heatRing.js \ src/component/punchCard.js \ src/component/numberCard.js \ src/component/title.js \ diff --git a/build/d3-ez.css b/build/d3-ez.css index 12eff5ab..fc9feb0e 100644 --- a/build/d3-ez.css +++ b/build/d3-ez.css @@ -52,7 +52,7 @@ .barGrouped rect.bar, .barStacked rect.bar { stroke: #999999; - stroke-width: 1px; + stroke-width: 0.5px; } .barGrouped rect.bar:hover, .barStacked rect.bar:hover { @@ -70,7 +70,7 @@ .chartRadialBar path.segment { fill: steelblue; - stroke-width: 1px; + stroke-width: 0.5px; stroke: #999999; } @@ -105,10 +105,16 @@ /* === Circular Heat Chart Styles === */ .chartCircularHeat path.segment { - stroke: #B6B6B6; + stroke: #999999; stroke-width: 0.5px; } +.chartCircularHeat path.segment:hover { + opacity: 0.7; + stroke: black; + cursor: pointer; +} + .chartCircularHeat .segmentLabels text { font-size: 1.0em; font-weight: bold; @@ -123,12 +129,12 @@ /* === Tabular Heat Chart Styles === */ -.chartTabularHeat rect.card { +.chartTabularHeat rect.cell { stroke: #999999; - stroke-width: 1px; + stroke-width: 0.5px; } -.chartTabularHeat rect.card:hover { +.chartTabularHeat rect.cell:hover { opacity: 0.7; stroke: black; cursor: pointer; @@ -143,7 +149,7 @@ .chartDonut path.slice { stroke: #999999; - stroke-width: 1px; + stroke-width: 0.5px; } .chartDonut path.slice:hover { @@ -164,7 +170,7 @@ .chartPunchCard circle.punchSpot { stroke: #999999; - stroke-width: 1px; + stroke-width: 0.5px; } .chartPunchCard circle.punchSpot:hover { diff --git a/build/d3-ez.js b/build/d3-ez.js index 2e6e8441..568a42ff 100644 --- a/build/d3-ez.js +++ b/build/d3-ez.js @@ -6,7 +6,7 @@ * @license GPLv3 */ d3.ez = { - version: "2.1.7", + version: "2.1.8", author: "James Saunders", copyright: "Copyright (C) 2017 James Saunders", license: "GPL-3.0" @@ -342,167 +342,6 @@ d3.ez.chart = function module() { return my; }; -/** - * Vega - * - * @example - * @todo - */ -d3.ez.vega = function module() { - // SVG and Canvas containers (Populated by 'my' function) - var svg; - var canvas; - // Default Options (Configurable via setters) - var width = 1e3; - var height = 600; - var margin = { - top: 30, - right: 30, - bottom: 30, - left: 30 - }; - var padding = 20; - var canvasW = 580; - var canvasH = 380; - var chartTop = 0; - var classed = "d3ez"; - var chart = undefined; - var yScale = undefined; - var yAxis = undefined; - var xScale = undefined; - var xAxis = undefined; - var legend = undefined; - var title = undefined; - var creditTag = d3.ez.component.creditTag(); - var yAxisLabel = ""; - // Colours - var colorScale = undefined; - // Dispatch (custom events) - var dispatch = d3.dispatch("customMouseOver", "customMouseOut", "customClick"); - function init(data) { - canvasW = width - (margin.left + margin.right); - canvasH = height - (margin.top + margin.bottom); - // Init Chart - chart.colorScale(colorScale).xScale(xScale).yScale(yScale).width(canvasW).height(canvasH).dispatch(dispatch); - // Init Legend - if (legend) { - legend.width(120).height(200); - chart.width(chart.width() - legend.width() - padding); - } - // Init Title - if (title) { - chartTop = title.height() + padding; - chart.height(chart.height() - title.height() - padding); - } - // Have we changed the graph size? - xScale.range([ chart.width(), 0 ]); - yScale.range([ chart.height(), 0 ]); - // Init Axis - xAxis = d3.axisBottom(xScale); - yAxis = d3.axisLeft(yScale); - // Init Credit Tag - creditTag.text("d3-ez.net").href("http://d3-ez.net"); - } - function my(selection) { - selection.each(function(data) { - init(data); - // Create SVG element (if it does not exist already) - if (!svg) { - svg = d3.select(this).append("svg").classed(classed, true).attr("width", width).attr("height", height); - canvas = svg.append("g").classed("canvas", true); - canvas.append("g").classed("chartbox", true); - canvas.select(".chartbox").append("g").classed("x-axis axis", true); - canvas.select(".chartbox").append("g").classed("y-axis axis", true); - canvas.append("g").classed("legendbox", true); - canvas.append("g").classed("titlebox", true); - svg.append("g").classed("creditbox", true); - } else { - canvas = svg.select(".canvas"); - } - // Update the canvas dimensions - canvas.attr("transform", "translate(" + margin.left + "," + margin.top + ")").attr("width", canvasW).attr("height", canvasH); - // Add Chart - canvas.select(".chartbox").attr("transform", "translate(0," + chartTop + ")").datum(data).call(chart); - canvas.select(".y-axis").call(yAxis); - canvas.select(".x-axis").attr("transform", "translate(0," + chart.height() + ")").call(xAxis); - // Add Title - if (title) { - canvas.select(".titlebox").attr("transform", "translate(" + width / 2 + "," + 0 + ")").call(title); - } - // Add Legend - if (legend && (typeof chart.colorScale === "function" || typeof chart.sizeScale === "function")) { - if (typeof chart.colorScale === "function") { - legend.colorScale(chart.colorScale()); - } - if (typeof chart.sizeScale === "function") { - legend.sizeScale(chart.sizeScale()); - } - canvas.select(".legendbox").attr("transform", "translate(" + (canvasW - legend.width()) + "," + chartTop + ")").call(legend); - } - // Add Credit Tag - if (creditTag) { - svg.select(".creditbox").attr("transform", "translate(" + (width - 10) + "," + (height - 5) + ")").call(creditTag); - } - }); - } - // Configuration Getters & Setters - my.width = function(_) { - if (!arguments.length) return width; - width = _; - return this; - }; - my.height = function(_) { - if (!arguments.length) return height; - height = _; - return this; - }; - my.chart = function(_) { - if (!arguments.length) return chart; - chart = _; - return this; - }; - my.colorScale = function(_) { - if (!arguments.length) return colorScale; - colorScale = _; - return this; - }; - my.xScale = function(_) { - if (!arguments.length) return xScale; - xScale = _; - return this; - }; - my.yScale = function(_) { - if (!arguments.length) return yScale; - yScale = _; - return this; - }; - my.legend = function(_) { - if (!arguments.length) return legend; - legend = _; - return this; - }; - my.title = function(_) { - if (!arguments.length) return title; - if (typeof _ === "string") { - // If the caller has passed a plain string convert it to a title object. - title = d3.ez.title().mainText(_).subText(""); - } else { - title = _; - } - return this; - }; - my.yAxisLabel = function(_) { - if (!arguments.length) return yAxisLabel; - yAxisLabel = _; - return this; - }; - my.on = function() { - var value = dispatch.on.apply(dispatch, arguments); - return value === dispatch ? my : value; - }; - return my; -}; - /** * Colour Palettes * @@ -909,10 +748,12 @@ d3.ez.component.donut = function module() { var defaultRadius = Math.min(width, height) / 2; radius = typeof radius === "undefined" ? defaultRadius : radius; innerRadius = typeof innerRadius === "undefined" ? defaultRadius / 2 : innerRadius; + // Pie Generator var pie = d3.pie().value(function(d) { return d.value; - }).sort(null); - var arc = d3.arc().innerRadius(innerRadius).outerRadius(radius).cornerRadius(3).padAngle(.015); + }).sort(null).padAngle(.015); + // Arc Generators + var arc = d3.arc().innerRadius(innerRadius).outerRadius(radius).cornerRadius(2); var outerArc = d3.arc().innerRadius(radius * .9).outerRadius(radius * .9); function arcTween(d) { var i = d3.interpolate(this._current, d); @@ -1461,8 +1302,8 @@ d3.ez.component.lineChart = function module() { */ d3.ez.component.heatMap = function module() { // Default Options (Configurable via setters) - var height = 100; var width = 300; + var height = 100; var colorScale = undefined; var xScale = undefined; var yScale = undefined; @@ -1475,24 +1316,24 @@ d3.ez.component.heatMap = function module() { selection.each(function(data) { var cellHeight = yScale.bandwidth(); var cellWidth = xScale.bandwidth(); - selection.selectAll(".deck").data(function(d) { + var heatMap = selection.selectAll(".heatMap").data(function(d) { return [ d ]; - }).enter().append("g").classed("deck", true).on("click", function(d) { + }).enter().append("g").classed("heatMap", true).on("click", function(d) { dispatch.call("customClick", this, d); }); - var deck = selection.selectAll(".deck"); - var cards = deck.selectAll(".card").data(function(d) { + heatMap = selection.selectAll(".heatMap").merge(heatMap); + var cells = heatMap.selectAll(".cell").data(function(d) { return d.values; }); - cards.enter().append("rect").attr("x", function(d, i) { + cells.enter().append("rect").attr("x", function(d, i) { return xScale(d.key); - }).attr("y", 0).attr("rx", 3).attr("ry", 3).attr("class", "card").attr("width", cellWidth).attr("height", cellHeight).on("click", dispatch.customClick).on("mouseover", function(d) { + }).attr("y", 0).attr("rx", 2).attr("ry", 2).attr("class", "cell").attr("width", cellWidth).attr("height", cellHeight).on("click", dispatch.customClick).on("mouseover", function(d) { dispatch.call("customMouseOver", this, d); - }).merge(cards).transition().duration(1e3).attr("fill", function(d) { + }).merge(cells).transition().duration(1e3).attr("fill", function(d) { return colorScale(d.value); }); - cards.exit().remove(); - cards.select("title").text(function(d) { + cells.exit().remove(); + cells.select("title").text(function(d) { return d.value; }); }); @@ -1536,18 +1377,20 @@ d3.ez.component.heatMap = function module() { }; /** - * Reusable Circular Heat Map + * Reusable Circular Heat Ring * * @example - * var myBars = d3.ez.component.heatCircle() + * var myBars = d3.ez.component.heatRing() * .colorScale(**D3 Scale Object**); * d3.select("svg").call(myBars); */ -d3.ez.component.heatCircle = function module() { +d3.ez.component.heatRing = function module() { // Default Options (Configurable via setters) var width = 400; var height = 300; var colorScale = undefined; + var xScale = undefined; + var yScale = undefined; var transition = { ease: d3.easeBounce, duration: 500 @@ -1557,68 +1400,37 @@ d3.ez.component.heatCircle = function module() { var innerRadius = undefined; function my(selection) { selection.each(function(data) { - // Slice Data, calculate totals, max etc. - var slicedData = d3.ez.dataParse(data); - groupNames = slicedData.groupNames; - numRadials = groupNames.length; - categoryNames = slicedData.categoryNames; - numSegments = categoryNames.length; var defaultRadius = Math.min(width, height) / 2; radius = typeof radius === "undefined" ? defaultRadius : radius; innerRadius = typeof innerRadius === "undefined" ? defaultRadius / 4 : innerRadius; - var segmentHeight = (radius - innerRadius) / numRadials; + // Pie Generator + var pie = d3.pie().value(function(d) { + return 1; + }).sort(null).padAngle(.015); // Arc Generator - var arc = d3.arc().innerRadius(function(d, i) { - return innerRadius + segmentHeight * d.ring; - }).outerRadius(function(d, i) { - return innerRadius + segmentHeight * (d.ring + 1); - }).startAngle(function(d, i) { - return i * 2 * Math.PI / numSegments; - }).endAngle(function(d, i) { - return (i + 1) * 2 * Math.PI / numSegments; - }); + var arc = d3.arc().outerRadius(radius).innerRadius(innerRadius).cornerRadius(2); // Create chart group - var circularHeat = selection.selectAll(".heatCircle").data(function(d) { + var heatRing = selection.selectAll(".heatRing").data(function(d) { return [ d ]; - }).enter().append("g").classed("heatCircle", true).on("click", function(d) { + }).enter().append("g").classed("heatRing", true).on("click", function(d) { dispatch.call("customClick", this, d); }); - circularHeat.append("g").attr("class", "rings"); - circularHeat.append("g").attr("class", "radialLabels"); - var circularHeat = selection.selectAll(".heatCircle").merge(circularHeat); - // Rings - circularHeat.select(".rings").selectAll("g").data(function(d) { - return d; - }).enter().append("g").classed("ring", true); + heatRing = selection.selectAll(".heatRing").merge(heatRing); + var segments = heatRing.selectAll(".segment").data(function(d) { + var key = d.key; + var data = pie(d.values); + data.forEach(function(d, i) { + data[i].key = key; + }); + return data; + }); // Ring Segments - circularHeat.selectAll(".ring").selectAll("path").data(function(d, i) { - // Add index (used to calculate ring number) - for (j = 0; j < numSegments; j++) { - d.values[j].ring = i; - } - return d.values; - }).enter().append("path").attr("d", arc).attr("fill", function(d) { - return colorScale(d.value); + segments.enter().append("path").attr("d", arc).attr("fill", function(d) { + return colorScale(d.data.value); }).classed("segment", true).on("mouseover", function(d) { - dispatch.call("customMouseOver", this, d); - }); - // Radial Labels - var lsa = .01; - // Label start angle - var radialLabels = circularHeat.select(".radialLabels").classed("labels", true); - radialLabels.selectAll("def").data(function(d) { - return d; - }).enter().append("def").append("path").attr("id", function(d, i) { - return "radial-label-path-" + i; - }).attr("d", function(d, i) { - var r = innerRadius + (i + .2) * segmentHeight; - return "m" + r * Math.sin(lsa) + " -" + r * Math.cos(lsa) + " a" + r + " " + r + " 0 1 1 -1 0"; - }); - radialLabels.selectAll("text").data(groupNames).enter().append("text").append("textPath").attr("xlink:href", function(d, i) { - return "#radial-label-path-" + i; - }).text(function(d) { - return d; + dispatch.call("customMouseOver", this, d.data); }); + segments.exit().remove(); }); } // Configuration Getters & Setters @@ -1647,6 +1459,16 @@ d3.ez.component.heatCircle = function module() { colorScale = _; return my; }; + my.xScale = function(_) { + if (!arguments.length) return xScale; + xScale = _; + return my; + }; + my.yScale = function(_) { + if (!arguments.length) return yScale; + yScale = _; + return my; + }; my.dispatch = function(_) { if (!arguments.length) return dispatch(); dispatch = _; @@ -2009,7 +1831,7 @@ d3.ez.component.circularLabels = function module() { selection.each(function(data) { var defaultRadius = Math.min(width, height) / 2; radius = typeof radius === "undefined" ? defaultRadius : radius; - var labelRadius = radius * 1.05; + var labelRadius = radius * 1.02; var circularLabels = selection.selectAll(".circularLabels").data(function(d) { return [ d ]; }).enter().append("g").classed("circularLabels", true); @@ -2216,21 +2038,13 @@ d3.ez.component.htmlList = function module() { /** * Discrete Bar Chart * - * @example - * var myChart = d3.ez.chart.discreteBar() - * .width(400) - * .height(300) - * .transition({ease: "bounce", duration: 1500}) - * .colors(d3.scaleCategory10().range()); - * d3.select("#chartholder") - * .datum(data) - * .call(myChart); */ d3.ez.chart.discreteBar = function module() { // SVG and Chart containers (Populated by 'my' function) var svg; var chart; // Default Options (Configurable via setters) + var classed = "chartDiscreteBar"; var width = 400; var height = 300; var margin = { @@ -2244,37 +2058,38 @@ d3.ez.chart.discreteBar = function module() { duration: 500 }; var colors = d3.ez.colors.categorical(4); - var gap = 0; - // Data Options (Populated by 'init' function) - var chartW = 0; - var chartH = 0; - var xScale = undefined; - var yScale = undefined; - var xAxis = undefined; - var yAxis = undefined; - var colorScale = undefined; - var yAxisLabel = undefined; + // Chart Dimensions + var chartW; + var chartH; + // Scales and Axis + var xScale; + var yScale; + var xAxis; + var yAxis; + var colorScale; // Dispatch (Custom events) var dispatch = d3.dispatch("customMouseOver", "customMouseOut", "customClick"); + // Other Customisation Options + var yAxisLabel = undefined; function init(data) { chartW = width - (margin.left + margin.right); chartH = height - (margin.top + margin.bottom); // Slice Data, calculate totals, max etc. var slicedData = d3.ez.dataParse(data); - var categoryNames = slicedData.categoryNames; - var maxValue = slicedData.maxValue; - var yAxisLabel = slicedData.groupName; + categoryNames = slicedData.categoryNames; + maxValue = slicedData.maxValue; + yAxisLabel = slicedData.groupName; + if (!colorScale) { + // If the colorScale has not already been passed + // then attempt to calculate. + colorScale = d3.scaleOrdinal().range(colors).domain(categoryNames); + } // X & Y Scales xScale = d3.scaleBand().domain(categoryNames).rangeRound([ 0, chartW ]).padding(.15); yScale = d3.scaleLinear().domain([ 0, maxValue ]).range([ chartH, 0 ]); // X & Y Axis xAxis = d3.axisBottom(xScale); yAxis = d3.axisLeft(yScale); - if (!colorScale) { - // If the colorScale has not already been passed - // then attempt to calculate. - colorScale = d3.scaleOrdinal().range(colors).domain(categoryNames); - } } function my(selection) { selection.each(function(data) { @@ -2298,7 +2113,7 @@ d3.ez.chart.discreteBar = function module() { chart = svg.select(".chart"); } // Update the chart dimensions - chart.classed("chartDiscreteBar", true).attr("transform", "translate(" + margin.left + "," + margin.top + ")").attr("width", chartW).attr("height", chartH); + chart.classed(classed, true).attr("transform", "translate(" + margin.left + "," + margin.top + ")").attr("width", chartW).attr("height", chartH); // Add axis to chart chart.select(".x-axis").attr("transform", "translate(0," + chartH + ")").call(xAxis); chart.select(".y-axis").call(yAxis); @@ -2353,21 +2168,13 @@ d3.ez.chart.discreteBar = function module() { /** * Grouped Bar Chart * - * @example - * var myChart = d3.ez.chart.groupedBar() - * .width(400) - * .height(300) - * .transition({ease: "bounce", duration: 1500}) - * .groupType("stacked"); - * d3.select("#chartholder") - * .datum(data) - * .call(myChart); */ d3.ez.chart.groupedBar = function module() { // SVG and Chart containers (Populated by 'my' function) var svg; var chart; // Default Options (Configurable via setters) + var classed = "chartGroupedBar"; var width = 400; var height = 300; var margin = { @@ -2381,29 +2188,41 @@ d3.ez.chart.groupedBar = function module() { duration: 500 }; var colors = d3.ez.colors.categorical(4); - var gap = 0; - var yAxisLabel = null; - var groupType = "clustered"; - // Data Options (Populated by 'init' function) - var chartW = 0; - var chartH = 0; - var xScale = undefined; - var xScale2 = undefined; - var yScale = undefined; - var xAxis = undefined; - var yAxis = undefined; - var colorScale = undefined; + // Chart Dimensions + var chartW; + var chartH; + // Scales and Axis + var xScale; + var xScale2; + var yScale; + var xAxis; + var yAxis; + var colorScale; + // Data Variables + var groupNames; + var groupTotalsMax; + var maxValue; + var categoryNames; // Dispatch (Custom events) var dispatch = d3.dispatch("customMouseOver", "customMouseOut", "customClick"); + // Other Customisation Options + var yAxisLabel = null; + var groupType = "clustered"; function init(data) { chartW = width - margin.left - margin.right; chartH = height - margin.top - margin.bottom; // Slice Data, calculate totals, max etc. var slicedData = d3.ez.dataParse(data); - var groupNames = slicedData.groupNames; - var groupTotalsMax = slicedData.groupTotalsMax; - var maxValue = slicedData.maxValue; - var categoryNames = slicedData.categoryNames; + groupNames = slicedData.groupNames; + groupTotalsMax = slicedData.groupTotalsMax; + maxValue = slicedData.maxValue; + categoryNames = slicedData.categoryNames; + // Colour Scale + if (!colorScale) { + // If the colorScale has not already been passed + // then attempt to calculate. + colorScale = d3.scaleOrdinal().range(colors).domain(categoryNames); + } // X & Y Scales xScale = d3.scaleBand().domain(groupNames).rangeRound([ 0, chartW ]).padding(.1); yScale = d3.scaleLinear().range([ chartH, 0 ]).domain([ 0, groupType === "stacked" ? groupTotalsMax : maxValue ]); @@ -2411,8 +2230,6 @@ d3.ez.chart.groupedBar = function module() { // X & Y Axis xAxis = d3.axisBottom(xScale); yAxis = d3.axisLeft(yScale); - // Colour Scale - colorScale = d3.scaleOrdinal().range(colors).domain(categoryNames); } function my(selection) { selection.each(function(data) { @@ -2436,7 +2253,7 @@ d3.ez.chart.groupedBar = function module() { chart = selection.select(".chart"); } // Update the chart dimensions - chart.classed("chartGroupedBar", true).attr("transform", "translate(" + margin.left + "," + margin.top + ")").attr("width", chartW).attr("height", chartH); + chart.classed(classed, true).attr("transform", "translate(" + margin.left + "," + margin.top + ")").attr("width", chartW).attr("height", chartH); // Add axis to chart chart.select(".x-axis").attr("transform", "translate(0," + chartH + ")").call(xAxis); chart.select(".y-axis").call(yAxis); @@ -2473,16 +2290,16 @@ d3.ez.chart.groupedBar = function module() { margin = _; return this; }; - my.yAxisLabel = function(_) { - if (!arguments.length) return yAxisLabel; - yAxisLabel = _; - return this; - }; my.groupType = function(_) { if (!arguments.length) return groupType; groupType = _; return this; }; + my.yAxisLabel = function(_) { + if (!arguments.length) return yAxisLabel; + yAxisLabel = _; + return this; + }; my.transition = function(_) { if (!arguments.length) return transition; transition = _; @@ -2513,19 +2330,13 @@ d3.ez.chart.groupedBar = function module() { /** * Radial Bar Chart * - * @example - * var myChart = d3.ez.chart.radialBar(); - * d3.select("#chartholder") - * .datum(data) - * .call(myChart); - * - * Credit: Peter Cook http://animateddata.co.uk/ */ d3.ez.chart.radialBar = function module() { // SVG and Chart containers (Populated by 'my' function) var svg; var chart; // Default Options (Configurable via setters) + var classed = "chartRadialBar"; var width = 400; var height = 300; var margin = { @@ -2539,31 +2350,44 @@ d3.ez.chart.radialBar = function module() { duration: 500 }; var colors = d3.ez.colors.categorical(4); - var radius = undefined; - var capitalizeLabels = false; - var colorLabels = false; - // Data Options (Populated by 'init' function) - var chartW = 0; - var chartH = 0; - var yScale = undefined; - var colorScale = undefined; + // Chart Dimensions + var chartW; + var chartH; + var radius; + var innerRadius; + // Scales and Axis + var xScale; + var yScale; + var yScale2; + var colorScale; + // Data Variables var categoryNames = []; var maxValue = 0; // Dispatch (Custom events) var dispatch = d3.dispatch("customMouseOver", "customMouseOut", "customClick"); + // Other Customisation Options + var capitalizeLabels = false; + var colorLabels = false; function init(data) { chartW = width - (margin.left + margin.right); chartH = height - (margin.top + margin.bottom); + var defaultRadius = Math.min(chartW, chartH) / 2; + radius = typeof radius === "undefined" ? defaultRadius : radius; + innerRadius = typeof innerRadius === "undefined" ? defaultRadius / 4 : innerRadius; // Slice Data, calculate totals, max etc. var slicedData = d3.ez.dataParse(data); - maxValue = slicedData.maxValue; - var domain = [ 0, maxValue ]; categoryNames = slicedData.categoryNames; + maxValue = slicedData.maxValue; + // Colour Scale + if (!colorScale) { + // If the colorScale has not already been passed + // then attempt to calculate. + colorScale = d3.scaleOrdinal().range(colors).domain(categoryNames); + } // X & Y Scales xScale = d3.scaleBand().domain(categoryNames).rangeRound([ 0, chartW ]).padding(.15); - yScale = d3.scaleLinear().domain(domain).range([ 0, radius ]); - // Colour Scale - colorScale = d3.scaleOrdinal().range(colors).domain(categoryNames); + yScale = d3.scaleLinear().domain([ 0, maxValue ]).range([ 0, radius ]); + yScale2 = d3.scaleLinear().domain([ 0, maxValue ]).range([ 0, -radius ]); } function my(selection) { selection.each(function(data) { @@ -2589,15 +2413,14 @@ d3.ez.chart.radialBar = function module() { chart = selection.select(".chart"); } // Update the chart dimensions - chart.classed("chartRadialBar", true).attr("transform", "translate(" + width / 2 + "," + height / 2 + ")").attr("width", width).attr("height", height); + chart.classed(classed, true).attr("transform", "translate(" + width / 2 + "," + height / 2 + ")").attr("width", chartW).attr("height", chartH); // Add the chart - var barRadial = d3.ez.component.barRadial().width(chartW).height(chartH).radius(radius).yScale(yScale).colorScale(colorScale).dispatch(dispatch); + var barRadial = d3.ez.component.barRadial().radius(radius).yScale(yScale).colorScale(colorScale).dispatch(dispatch); chart.select(".barChart").datum(data).call(barRadial); // Circular Axis var circularAxis = d3.ez.component.circularAxis().xScale(xScale).yScale(yScale).width(chartW).height(chartH).radius(radius); chart.select(".circleAxis").call(circularAxis); // Y Axis - var yScale2 = d3.scaleLinear().domain(yScale.domain()).range([ 0, -radius ]); var yAxis = d3.axisLeft(yScale2); chart.select(".axis").call(yAxis); // Circular Labels @@ -2609,19 +2432,16 @@ d3.ez.chart.radialBar = function module() { my.width = function(_) { if (!arguments.length) return width; width = _; - radius = d3.min([ width - (margin.right + margin.left), height - (margin.top + margin.bottom) ]) / 2; return this; }; my.height = function(_) { if (!arguments.length) return height; height = _; - radius = d3.min([ width - (margin.right + margin.left), height - (margin.top + margin.bottom) ]) / 2; return this; }; my.margin = function(_) { if (!arguments.length) return margin; margin = _; - radius = d3.min([ width - (margin.right + margin.left), height - (margin.top + margin.bottom) ]) / 2; return this; }; my.radius = function(_) { @@ -2669,19 +2489,13 @@ d3.ez.chart.radialBar = function module() { /** * Circular Heat Chart * - * @example - * var myChart = d3.ez.chart.circularHeat(); - * d3.select("#chartholder") - * .datum(data) - * .call(myChart); - * - * Credit: Peter Cook http://animateddata.co.uk/ */ d3.ez.chart.circularHeat = function module() { // SVG and Chart containers (Populated by 'my' function) var svg; var chart; // Default Options (Configurable via setters) + var classed = "chartCircularHeat"; var width = 400; var height = 300; var margin = { @@ -2694,36 +2508,52 @@ d3.ez.chart.circularHeat = function module() { ease: d3.easeBounce, duration: 500 }; - var classed = "chartCircularHeat"; var colors = [ d3.rgb(214, 245, 0), d3.rgb(255, 166, 0), d3.rgb(255, 97, 0), d3.rgb(200, 65, 65) ]; - var radius = undefined; - var innerRadius = undefined; - // Data Options (Populated by 'init' function) + // Chart Dimensions + var chartW; + var chartH; + var radius; + var innerRadius; + // Scales and Axis + var xScale; + var yScale; + var yScale2; + var colorScale; + // Data Variables + var categoryNames = []; + var groupNames = []; var minValue = 0; var maxValue = 0; - var radialLabels = []; - var numRadials = 24; - var segmentLabels = []; - var numSegments = 24; - var segmentHeight = 0; - var colorScale = undefined; var thresholds = undefined; - var categoryNames = []; // Dispatch (Custom events) var dispatch = d3.dispatch("customMouseOver", "customMouseOut", "customClick"); function init(data) { chartW = width - (margin.left + margin.right); chartH = height - (margin.top + margin.bottom); + var defaultRadius = Math.min(chartW, chartH) / 2; + radius = typeof radius === "undefined" ? defaultRadius : radius; + innerRadius = typeof innerRadius === "undefined" ? defaultRadius / 4 : innerRadius; // Slice Data, calculate totals, max etc. var slicedData = d3.ez.dataParse(data); + maxValue = slicedData.maxValue; + minValue = slicedData.minValue; categoryNames = slicedData.categoryNames; + groupNames = slicedData.groupNames; // If thresholds values are not already set // attempt to auto-calculate some thresholds. if (!thresholds) { var thresholds = slicedData.thresholds; } // Colour Scale - colorScale = d3.scaleThreshold().domain(thresholds).range(colors); + if (!colorScale) { + // If the colorScale has not already been passed + // then attempt to calculate. + colorScale = d3.scaleThreshold().range(colors).domain(thresholds); + } + // X & Y Scales + xScale = d3.scaleBand().domain(categoryNames).rangeRound([ 0, chartW ]).padding(.1); + yScale = d3.scaleBand().domain(groupNames).rangeRound([ radius, innerRadius ]).padding(.1); + yScale2 = d3.scaleBand().domain(groupNames).rangeRound([ -innerRadius, -radius ]).padding(.1); } function my(selection) { selection.each(function(data) { @@ -2741,16 +2571,32 @@ d3.ez.chart.circularHeat = function module() { }(d3.select(this)); svg.classed("d3ez", true).attr("width", width).attr("height", height); chart = svg.append("g").classed("chart", true); + chart.append("g").classed("circleRings", true); + chart.append("g").classed("circleLabels", true); + chart.append("g").classed("axis", true); } else { chart = svg.select(".chart"); } // Update the chart dimensions - chart.classed("chartCircularHeat", true).attr("transform", "translate(" + width / 2 + "," + height / 2 + ")").attr("width", chartW).attr("height", chartH); - var heatMap = d3.ez.component.heatCircle().width(chartW).height(chartH).colorScale(colorScale).radius(radius).innerRadius(innerRadius).dispatch(dispatch); - chart.datum(data).call(heatMap); + chart.classed(classed, true).attr("transform", "translate(" + width / 2 + "," + height / 2 + ")").attr("width", chartW).attr("height", chartH); + var heatRing = d3.ez.component.heatRing().radius(function(d) { + return yScale(d.key); + }).innerRadius(function(d) { + return yScale(d.key) + yScale.bandwidth(); + }).colorScale(colorScale).yScale(yScale).xScale(xScale).dispatch(dispatch); + var series = chart.select(".circleRings").selectAll(".series").data(function(d) { + return d; + }).enter().append("g").attr("class", "series"); + series.datum(function(d) { + return d; + }).call(heatRing); + series.exit().remove(); // Circular Labels var circularLabels = d3.ez.component.circularLabels().width(chartW).height(chartH).radius(radius); - chart.datum(categoryNames).call(circularLabels); + chart.select(".circleLabels").datum(categoryNames).call(circularLabels); + // Y Axis + var yAxis = d3.axisLeft(yScale2); + chart.select(".axis").call(yAxis); }); } // Configuration Getters & Setters @@ -2809,38 +2655,40 @@ d3.ez.chart.circularHeat = function module() { /** * Tabular Heat Chart * - * @example - * var myChart = d3.ez.chart.tabularHeat(); - * d3.select("#chartholder") - * .datum(data) - * .call(myChart); */ d3.ez.chart.tabularHeat = function module() { // SVG and Chart containers (Populated by 'my' function) var svg; var chart; // Default Options (Configurable via setters) - var width = 600; - var height = 600; + var classed = "chartTabularHeat"; + var width = 400; + var height = 300; var margin = { - top: 50, - right: 40, - bottom: 40, - left: 40 + top: 45, + right: 20, + bottom: 20, + left: 45 }; var transition = { ease: d3.easeBounce, duration: 500 }; var colors = [ d3.rgb(214, 245, 0), d3.rgb(255, 166, 0), d3.rgb(255, 97, 0), d3.rgb(200, 65, 65) ]; - // Data Options (Populated by 'init' function) - var chartW = 0; - var chartH = 0; - var xScale = undefined; - var yScale = undefined; - var xAxis = undefined; - var yAxis = undefined; - var colorScale = undefined; + // Chart Dimensions + var chartW; + var chartH; + // Scales and Axis + var xScale; + var yScale; + var colorScale; + var xAxis; + var yAxis; + // Data Variables + var groupNames = []; + var categoryNames = []; + var minValue = 0; + var maxValue = 0; var thresholds = undefined; // Dispatch (Custom events) var dispatch = d3.dispatch("customMouseOver", "customMouseOut", "customClick"); @@ -2849,23 +2697,27 @@ d3.ez.chart.tabularHeat = function module() { chartH = height - margin.top - margin.bottom; // Slice Data, calculate totals, max etc. var slicedData = d3.ez.dataParse(data); - var maxValue = slicedData.maxValue; - var minValue = slicedData.minValue; - var categoryNames = slicedData.categoryNames; - var groupNames = slicedData.groupNames; + maxValue = slicedData.maxValue; + minValue = slicedData.minValue; + categoryNames = slicedData.categoryNames; + groupNames = slicedData.groupNames; // If thresholds values are not already set // attempt to auto-calculate some thresholds. if (!thresholds) { var thresholds = slicedData.thresholds; } + // Colour Scale + if (!colorScale) { + // If the colorScale has not already been passed + // then attempt to calculate. + colorScale = d3.scaleThreshold().domain(thresholds).range(colors); + } // X & Y Scales xScale = d3.scaleBand().domain(categoryNames).rangeRound([ 0, chartW ]).padding(.05); yScale = d3.scaleBand().domain(groupNames).rangeRound([ 0, chartH ]).padding(.05); // X & Y Axis xAxis = d3.axisTop(xScale); yAxis = d3.axisLeft(yScale); - // Colour Scale - colorScale = d3.scaleThreshold().domain(thresholds).range(colors); } function my(selection) { selection.each(function(data) { @@ -2889,7 +2741,7 @@ d3.ez.chart.tabularHeat = function module() { chart = selection.select(".chart"); } // Update the chart dimensions - chart.classed("chartTabularHeat", true).attr("transform", "translate(" + margin.left + "," + margin.top + ")").attr("width", chartW).attr("height", chartH); + chart.classed(classed, true).attr("transform", "translate(" + margin.left + "," + margin.top + ")").attr("width", chartW).attr("height", chartH); // Add axis to chart chart.select(".x-axis").call(xAxis).selectAll("text").attr("y", 0).attr("x", -8).attr("transform", "rotate(60)").style("text-anchor", "end"); chart.select(".y-axis").call(yAxis); @@ -2936,11 +2788,6 @@ d3.ez.chart.tabularHeat = function module() { thresholds = _; return this; }; - my.accessor = function(_) { - if (!arguments.length) return accessor; - accessor = _; - return this; - }; my.dispatch = function(_) { if (!arguments.length) return dispatch(); dispatch = _; @@ -2956,21 +2803,13 @@ d3.ez.chart.tabularHeat = function module() { /** * Donut Chart * - * @example - * var myChart = d3.ez.chart.donut() - * .width(400) - * .height(300) - * .radius(200) - * .innerRadius(50); - * d3.select("#chartholder") - * .datum(data) - * .call(myChart); */ d3.ez.chart.donut = function module() { // SVG and Chart containers (Populated by 'my' function) var svg; var chart; // Default Options (Configurable via setters) + var classed = "chartDonut"; var width = 400; var height = 300; var margin = { @@ -2984,20 +2823,27 @@ d3.ez.chart.donut = function module() { duration: 750 }; var colors = d3.ez.colors.categorical(4); - var radius = undefined; - var innerRadius = undefined; - // Data Options (Populated by 'init' function) - var chartW = 0; - var chartH = 0; - var colorScale = undefined; + // Chart Dimensions + var chartW; + var chartH; + var radius; + var innerRadius; + // Scales and Axis + var colorScale; + // Data Variables + var categoryNames = []; // Dispatch (Custom events) var dispatch = d3.dispatch("customMouseOver", "customMouseOut", "customClick"); function init(data) { chartW = width - (margin.left + margin.right); chartH = height - (margin.top + margin.bottom); + var defaultRadius = Math.min(chartW, chartH) / 2; + radius = typeof radius === "undefined" ? defaultRadius : radius; + innerRadius = typeof innerRadius === "undefined" ? defaultRadius / 2 : innerRadius; // Slice Data, calculate totals, max etc. var slicedData = d3.ez.dataParse(data); - var categoryNames = slicedData.categoryNames; + categoryNames = slicedData.categoryNames; + // Colour Scale if (!colorScale) { // If the colorScale has not already been passed // then attempt to calculate. @@ -3024,9 +2870,9 @@ d3.ez.chart.donut = function module() { chart = svg.select(".chart"); } // Update the chart dimensions - chart.classed("chartDonut", true).attr("transform", "translate(" + width / 2 + "," + height / 2 + ")").attr("width", chartW).attr("height", chartH); + chart.classed(classed, true).attr("transform", "translate(" + width / 2 + "," + height / 2 + ")").attr("width", chartW).attr("height", chartH); // Add the chart - var donutChart = d3.ez.component.donut().width(chartW).height(chartH).radius(radius).innerRadius(innerRadius).colorScale(colorScale).dispatch(dispatch); + var donutChart = d3.ez.component.donut().radius(radius).innerRadius(innerRadius).colorScale(colorScale).dispatch(dispatch); chart.datum(data).call(donutChart); }); } @@ -3084,81 +2930,80 @@ d3.ez.chart.donut = function module() { }; /** - * Punchcard + * Punch Card * - * @example - * var myChart = d3.ez.chart.punchCard() - * .width(600) - * .height(350) - * .color("green") - * .minRadius(5) - * .maxRadius(19) - * .useGlobalScale(true); - * d3.select("#chartholder") - * .datum(data) - * .call(myChart); */ d3.ez.chart.punchCard = function module() { // SVG and Chart containers (Populated by 'my' function) var svg; var chart; // Default Options (Configurable via setters) + var classed = "chartPunchCard"; var width = 400; var height = 300; var margin = { - top: 50, - right: 40, - bottom: 40, - left: 40 + top: 45, + right: 20, + bottom: 20, + left: 45 }; var transition = { ease: d3.easeBounce, duration: 500 }; - var color = "steelblue"; - var sizeScale = undefined; + var colors = [ d3.rgb("steelblue").brighter(), d3.rgb("steelblue").darker() ]; + // Chart Dimensions + var chartW; + var chartH; + // Scales and Axis + var sizeScale; + var xScale; + var yScale; + var xAxis; + var yAxis; + var colorScale; + // Data Variables + var maxValue; + var minValue; + var categoryNames; + var groupNames; + // Dispatch (Custom events) + var dispatch = d3.dispatch("customMouseOver", "customMouseOut", "customClick"); + // Misc Options var minRadius = 2; var maxRadius = 20; var formatTick = d3.format("0000"); var useGlobalScale = true; - // Data Options (Populated by 'init' function) - var chartW = 0; - var chartH = 0; - var xScale = undefined; - var yScale = undefined; - var xAxis = undefined; - var yAxis = undefined; - var colorScale = undefined; - // Dispatch (Custom events) - var dispatch = d3.dispatch("customMouseOver", "customMouseOut", "customClick"); function init(data) { chartW = width - margin.left - margin.right; chartH = height - margin.top - margin.bottom; // Slice Data, calculate totals, max etc. var slicedData = d3.ez.dataParse(data); - var maxValue = slicedData.maxValue; - var minValue = slicedData.minValue; - var categoryNames = slicedData.categoryNames; - var groupNames = slicedData.groupNames; + maxValue = slicedData.maxValue; + minValue = slicedData.minValue; + categoryNames = slicedData.categoryNames; + groupNames = slicedData.groupNames; valDomain = [ minValue, maxValue ]; sizeDomain = useGlobalScale ? valDomain : [ 0, d3.max(data[1]["values"], function(d) { return d["value"]; }) ]; + // Colour Scale + if (!colorScale) { + // If the colorScale has not already been passed + // then attempt to calculate. + colorScale = d3.scaleLinear().domain(valDomain).range(colors); + } // X & Y Scales xScale = d3.scaleBand().domain(categoryNames).rangeRound([ 0, chartW ]).padding(.05); yScale = d3.scaleBand().domain(groupNames).rangeRound([ 0, chartH ]).padding(.05); // X & Y Axis xAxis = d3.axisTop(xScale); yAxis = d3.axisLeft(yScale); - // Colour Scale - colorScale = d3.scaleLinear().domain(valDomain).range([ d3.rgb(color).brighter(), d3.rgb(color).darker() ]); // Size Scale sizeScale = d3.scaleLinear().domain(sizeDomain).range([ minRadius, maxRadius ]); } function my(selection) { selection.each(function(data) { - // If it is a single object, wrap it in an array - if (data.constructor !== Array) data = [ data ]; // Initialise Data init(data); // Create SVG and Chart containers (if they do not already exist) @@ -3179,7 +3024,7 @@ d3.ez.chart.punchCard = function module() { chart = selection.select(".chart"); } // Update the chart dimensions - chart.classed("chartPunchCard", true).attr("transform", "translate(" + margin.left + "," + margin.top + ")").attr("width", width).attr("height", height); + chart.classed(classed, true).attr("transform", "translate(" + margin.left + "," + margin.top + ")").attr("width", chartW).attr("height", chartH); // Add axis to chart chart.select(".x-axis").call(xAxis).selectAll("text").attr("y", 0).attr("x", -8).attr("transform", "rotate(60)").style("text-anchor", "end"); chart.select(".y-axis").call(yAxis); @@ -3221,16 +3066,16 @@ d3.ez.chart.punchCard = function module() { maxRadius = _; return this; }; - my.color = function(_) { - if (!arguments.length) return color; - color = _; - return this; - }; my.sizeScale = function(_) { if (!arguments.length) return sizeScale; sizeScale = _; return this; }; + my.colors = function(_) { + if (!arguments.length) return colors; + colors = _; + return this; + }; my.useGlobalScale = function(_) { if (!arguments.length) return useGlobalScale; useGlobalScale = _; @@ -3251,19 +3096,13 @@ d3.ez.chart.punchCard = function module() { /** * Multi Series Line Chart * - * @example - * var myChart = d3.ez.chart.multiSeriesLine() - * .width(400) - * .height(300); - * d3.select("#chartholder") - * .datum(data) - * .call(myChart); */ d3.ez.chart.multiSeriesLine = function module() { // SVG and Chart containers (Populated by 'my' function) var svg; var chart; // Default Options (Configurable via setters) + var classed = "chartMultiSeriesLine"; var width = 400; var height = 300; var margin = { @@ -3272,26 +3111,34 @@ d3.ez.chart.multiSeriesLine = function module() { bottom: 40, left: 40 }; + var transition = { + ease: d3.easeBounce, + duration: 500 + }; var colors = d3.ez.colors.categorical(3); - var yAxisLabel = null; - var groupType = "clustered"; - // Data Options (Populated by 'init' function) - var chartW = 0; - var chartH = 0; - var xScale = undefined; - var yScale = undefined; - var xAxis = undefined; - var yAxis = undefined; - var colorScale = undefined; + // Chart Dimensions + var chartW; + var chartH; + // Scales and Axis + var xScale; + var yScale; + var xAxis; + var yAxis; + var colorScale; + // Data Variables + var maxValue; + var groupNames; // Dispatch (Custom events) var dispatch = d3.dispatch("customMouseOver", "customMouseOut", "customClick"); + // Other Customisation Options + var yAxisLabel = null; function init(data) { chartW = width - margin.left - margin.right; chartH = height - margin.top - margin.bottom; // Slice Data, calculate totals, max etc. var slicedData = d3.ez.dataParse(data); - var maxValue = slicedData.maxValue; - var seriesNames = slicedData.groupNames; + maxValue = slicedData.maxValue; + groupNames = slicedData.groupNames; // Convert dates data.forEach(function(d, i) { d.values.forEach(function(b, j) { @@ -3301,14 +3148,18 @@ d3.ez.chart.multiSeriesLine = function module() { dateDomain = d3.extent(data[0].values, function(d) { return d.key; }); + // Colour Scale + if (!colorScale) { + // If the colorScale has not already been passed + // then attempt to calculate. + colorScale = d3.scaleOrdinal().range(colors).domain(groupNames); + } // X & Y Scales xScale = d3.scaleTime().range([ 0, chartW ]).domain(dateDomain); yScale = d3.scaleLinear().range([ chartH, 0 ]).domain([ 0, maxValue * 1.05 ]); // X & Y Axis xAxis = d3.axisBottom(xScale).tickFormat(d3.timeFormat("%d-%b-%y")); yAxis = d3.axisLeft(yScale); - // Colour Scale - colorScale = d3.scaleOrdinal().range(colors).domain(seriesNames); } function my(selection) { selection.each(function(data) { @@ -3332,7 +3183,7 @@ d3.ez.chart.multiSeriesLine = function module() { chart = selection.select(".chart"); } // Update the chart dimensions - chart.classed("chartMultiSeriesLine", true).attr("transform", "translate(" + margin.left + "," + margin.top + ")").attr("width", chartW).attr("height", chartH); + chart.classed(classed, true).attr("transform", "translate(" + margin.left + "," + margin.top + ")").attr("width", chartW).attr("height", chartH); // Add axis to chart chart.select(".x-axis").attr("transform", "translate(0," + chartH + ")").call(xAxis).selectAll("text").style("text-anchor", "end").attr("dx", "-.8em").attr("dy", ".15em").attr("transform", "rotate(-65)"); chart.select(".y-axis").call(yAxis); @@ -3370,11 +3221,6 @@ d3.ez.chart.multiSeriesLine = function module() { yAxisLabel = _; return this; }; - my.groupType = function(_) { - if (!arguments.length) return groupType; - groupType = _; - return this; - }; my.transition = function(_) { if (!arguments.length) return transition; transition = _; diff --git a/build/d3-ez.min.js b/build/d3-ez.min.js index 41076113..08cfa7b5 100644 --- a/build/d3-ez.min.js +++ b/build/d3-ez.min.js @@ -1 +1 @@ -d3.ez={version:"2.1.7",author:"James Saunders",copyright:"Copyright (C) 2017 James Saunders",license:"GPL-3.0"};d3.ez.component={description:"Reusable Components"};d3.ez.dataParse=function module(data){var levels=function(){if(data["key"]!=undefined){return 1}else{return 2}}();var groupName=function(){if(1==levels){var ret=d3.values(data)[0]}else{var ret=undefined}return ret}();var groupNames=function(){if(1==levels){ret=undefined}else{var ret=data.map(function(d){return d.key})}return ret}();var groupTotals=function(){if(1==levels){var ret=undefined}else{var ret={};d3.map(data).values().forEach(function(d,i){var groupName=d.key;d.values.forEach(function(d,i){var categoryValue=+d.value;ret[groupName]=typeof ret[groupName]==="undefined"?0:ret[groupName];ret[groupName]+=categoryValue})})}return ret}();var groupTotalsMax=function(){if(1==levels){var ret=undefined}else{var ret=d3.max(d3.values(groupTotals))}return ret}();var union=function(){var arrs=[].slice.call(arguments);var ret=[];for(var i=0,l=arrs.length;i-1){ret.splice(ret.indexOf(arrs[i][j-1])+1,0,currEl)}else{ret.push(currEl)}}}}return ret};var categoryNames=function(){if(1==levels){var ret=d3.values(data)[1].map(function(d){return d.key})}else{var ret=[];d3.map(data).values().forEach(function(d,i){var groupName=d.key;d.values.forEach(function(d,i){categoryName=d.key;ret[i]=categoryName});ret=union(ret)})}return ret}();var categoryTotal=function(){if(1==levels){var ret=d3.sum(data.values,function(d){return d.value})}else{var ret=undefined}return ret}();var categoryTotals=function(){if(1==levels){var ret=undefined}else{var ret={};d3.map(data).values().forEach(function(d,i){var groupName=d.key;d.values.forEach(function(d,i){var categoryName=d.key;var categoryValue=+d.value;ret[categoryName]=typeof ret[categoryName]==="undefined"?0:ret[categoryName];ret[categoryName]+=categoryValue})})}return ret}();var categoryTotalsMax=function(){if(1==levels){var ret=undefined}else{var ret=d3.max(d3.values(categoryTotals))}return ret}();var minValue=function(){if(1==levels){var ret=d3.min(data.values,function(d){return d.value})}else{var ret=undefined;d3.map(data).values().forEach(function(d,i){d.values.forEach(function(d,i){ret=typeof ret==="undefined"?d.value:d3.min([ret,d.value])})})}return+ret}();var maxValue=function(){if(1==levels){var ret=d3.max(data.values,function(d){return d.value})}else{var ret=undefined;d3.map(data).values().forEach(function(d,i){d.values.forEach(function(d,i){ret=typeof ret==="undefined"?d.value:d3.max([ret,d.value])})})}return+ret}();var decimalPlaces=function(num){var match=(""+num).match(/(?:\.(\d+))?(?:[eE]([+-]?\d+))?$/);if(!match){return 0}ret=Math.max(0,(match[1]?match[1].length:0)-(match[2]?+match[2]:0));return ret};var maxDecimalPlace=function(){if(1==levels){var ret=undefined}else{var ret=0;d3.map(data).values().forEach(function(d){d.values.forEach(function(d){ret=d3.max([ret,decimalPlaces(d.value)])})})}return ret}();var thresholds=function(){var distance=maxValue-minValue;var ret=[(minValue+.15*distance).toFixed(maxDecimalPlace),(minValue+.4*distance).toFixed(maxDecimalPlace),(minValue+.55*distance).toFixed(maxDecimalPlace),(minValue+.9*distance).toFixed(maxDecimalPlace)];return ret}();var my={levels:levels,groupName:groupName,groupNames:groupNames,groupTotals:groupTotals,groupTotalsMax:groupTotalsMax,categoryNames:categoryNames,categoryTotal:categoryTotal,categoryTotals:categoryTotals,categoryTotalsMax:categoryTotalsMax,minValue:minValue,maxValue:maxValue,maxDecimalPlace:maxDecimalPlace,thresholds:thresholds};return my};d3.ez.chart=function module(){var svg;var canvas;var width=600;var height=400;var margin={top:15,right:15,bottom:15,left:15};var canvasW=580;var canvasH=380;var chartTop=0;var classed="d3ez";var chart=undefined;var legend=undefined;var title=undefined;var creditTag=d3.ez.component.creditTag();var description="";var yAxisLabel="";var colorScale=undefined;var dispatch=d3.dispatch("customMouseOver","customMouseOut","customClick");function init(data){canvasW=width-(margin.left+margin.right);canvasH=height-(margin.top+margin.bottom);chart.dispatch(dispatch).width(canvasW).height(canvasH);if(legend){legend.width(150).height(200);chart.width(chart.width()-legend.width())}if(title){chartTop=title.height();chart.height(chart.height()-title.height())}creditTag.text("d3-ez.net").href("http://d3-ez.net")}function my(selection){selection.each(function(data){init(data);if(!svg){svg=d3.select(this).append("svg").classed(classed,true).attr("width",width).attr("height",height);canvas=svg.append("g").classed("canvas",true);canvas.append("g").classed("chartbox",true);canvas.append("g").classed("legendbox",true);canvas.append("g").classed("titlebox",true);canvas.append("g").classed("creditbox",true)}else{canvas=svg.select(".canvas")}canvas.attr("transform","translate("+margin.left+","+margin.top+")").attr("width",canvasW).attr("height",canvasH);canvas.select(".chartbox").datum(data).attr("transform","translate("+0+","+chartTop+")").call(chart);if(legend&&(typeof chart.colorScale==="function"||typeof chart.sizeScale==="function")){if(typeof chart.colorScale==="function"){legend.colorScale(chart.colorScale())}if(typeof chart.sizeScale==="function"){legend.sizeScale(chart.sizeScale())}canvas.select(".legendbox").attr("transform","translate("+(canvasW-legend.width())+","+title.height()+")").call(legend)}if(title){canvas.select(".titlebox").attr("transform","translate("+width/2+","+0+")").call(title)}canvas.select(".creditbox").attr("transform","translate("+(width-20)+","+(height-20)+")").call(creditTag)})}my.width=function(_){if(!arguments.length)return width;width=_;return this};my.height=function(_){if(!arguments.length)return height;height=_;return this};my.chart=function(_){if(!arguments.length)return chart;chart=_;return this};my.legend=function(_){if(!arguments.length)return legend;legend=_;return this};my.title=function(_){if(!arguments.length)return title;if(typeof _==="string"){title=d3.ez.title().mainText(_).subText("")}else{title=_}return this};my.yAxisLabel=function(_){if(!arguments.length)return yAxisLabel;yAxisLabel=_;return this};my.on=function(){var value=dispatch.on.apply(dispatch,arguments);return value===dispatch?my:value};return my};d3.ez.vega=function module(){var svg;var canvas;var width=1e3;var height=600;var margin={top:30,right:30,bottom:30,left:30};var padding=20;var canvasW=580;var canvasH=380;var chartTop=0;var classed="d3ez";var chart=undefined;var yScale=undefined;var yAxis=undefined;var xScale=undefined;var xAxis=undefined;var legend=undefined;var title=undefined;var creditTag=d3.ez.component.creditTag();var yAxisLabel="";var colorScale=undefined;var dispatch=d3.dispatch("customMouseOver","customMouseOut","customClick");function init(data){canvasW=width-(margin.left+margin.right);canvasH=height-(margin.top+margin.bottom);chart.colorScale(colorScale).xScale(xScale).yScale(yScale).width(canvasW).height(canvasH).dispatch(dispatch);if(legend){legend.width(120).height(200);chart.width(chart.width()-legend.width()-padding)}if(title){chartTop=title.height()+padding;chart.height(chart.height()-title.height()-padding)}xScale.range([chart.width(),0]);yScale.range([chart.height(),0]);xAxis=d3.axisBottom(xScale);yAxis=d3.axisLeft(yScale);creditTag.text("d3-ez.net").href("http://d3-ez.net")}function my(selection){selection.each(function(data){init(data);if(!svg){svg=d3.select(this).append("svg").classed(classed,true).attr("width",width).attr("height",height);canvas=svg.append("g").classed("canvas",true);canvas.append("g").classed("chartbox",true);canvas.select(".chartbox").append("g").classed("x-axis axis",true);canvas.select(".chartbox").append("g").classed("y-axis axis",true);canvas.append("g").classed("legendbox",true);canvas.append("g").classed("titlebox",true);svg.append("g").classed("creditbox",true)}else{canvas=svg.select(".canvas")}canvas.attr("transform","translate("+margin.left+","+margin.top+")").attr("width",canvasW).attr("height",canvasH);canvas.select(".chartbox").attr("transform","translate(0,"+chartTop+")").datum(data).call(chart);canvas.select(".y-axis").call(yAxis);canvas.select(".x-axis").attr("transform","translate(0,"+chart.height()+")").call(xAxis);if(title){canvas.select(".titlebox").attr("transform","translate("+width/2+","+0+")").call(title)}if(legend&&(typeof chart.colorScale==="function"||typeof chart.sizeScale==="function")){if(typeof chart.colorScale==="function"){legend.colorScale(chart.colorScale())}if(typeof chart.sizeScale==="function"){legend.sizeScale(chart.sizeScale())}canvas.select(".legendbox").attr("transform","translate("+(canvasW-legend.width())+","+chartTop+")").call(legend)}if(creditTag){svg.select(".creditbox").attr("transform","translate("+(width-10)+","+(height-5)+")").call(creditTag)}})}my.width=function(_){if(!arguments.length)return width;width=_;return this};my.height=function(_){if(!arguments.length)return height;height=_;return this};my.chart=function(_){if(!arguments.length)return chart;chart=_;return this};my.colorScale=function(_){if(!arguments.length)return colorScale;colorScale=_;return this};my.xScale=function(_){if(!arguments.length)return xScale;xScale=_;return this};my.yScale=function(_){if(!arguments.length)return yScale;yScale=_;return this};my.legend=function(_){if(!arguments.length)return legend;legend=_;return this};my.title=function(_){if(!arguments.length)return title;if(typeof _==="string"){title=d3.ez.title().mainText(_).subText("")}else{title=_}return this};my.yAxisLabel=function(_){if(!arguments.length)return yAxisLabel;yAxisLabel=_;return this};my.on=function(){var value=dispatch.on.apply(dispatch,arguments);return value===dispatch?my:value};return my};d3.ez.colors={categorical:function(scheme){switch(scheme){case 1:return["#5da5da","#faa43a","#60bd68","#f17cb0","#b2912f","#b276b2","#decf3f","#f15854","#4d4d4d"];case 2:return["#fbb4ae","#b3cde3","#ccebc5","#decbe4","#fed9a6","#ffffcc","#e5d8bd","#fddaec","#f2f2f2"];case 3:return["#3f51b5","#ff9800","#8bc34a","#9c27b0","#ffeb3b","#03a9f4","#f44336","#009688","#795548"];case 4:return d3.ez.colors.lumShift(d3.ez.colors.lumShift(d3.ez.colors.categorical(3),-.8),5.5)}},diverging:function(scheme){switch(scheme){case 1:return["#8c510a","#bf812d","#dfc27d","#f6e8c3","#f5f5f5","#c7eae5","#80cdc1","#35978f","#01665e"];case 2:return["#d73027","#f46d43","#fdae61","#fee08b","#ffffbf","#d9ef8b","#a6d96a","#66bd63","#1a9850"];case 3:return["#0000ff","#8052fe","#b58bfb","#ddc5f7","#fffff0","#ffcfb4","#ff9e7a","#ff6842","#ff0000"]}},sequential:function(origHex,count){var lumStep=.1;var lumMax=lumStep*count/2;var lumMin=0-lumMax;var lumScale=d3.scale.linear().domain([1,count]).range([lumMin,lumMax]);var result=[];for(var i=1;i<=count;i++){lum=lumScale(i);origHex=String(origHex).replace(/[^0-9a-f]/gi,"");if(origHex.length<6){origHex=origHex[0]+origHex[0]+origHex[1]+origHex[1]+origHex[2]+origHex[2]}var newHex="#";var c;for(var j=0;j<3;j++){c=parseInt(origHex.substr(j*2,2),16);c=Math.round(Math.min(Math.max(0,c+c*lum),255)).toString(16);newHex+=("00"+c).substr(c.length)}result.push(newHex)}return result},lumShift:function(colors,lum){var result=[];colors.forEach(function addNumber(origHex,index){origHex=String(origHex).replace(/[^0-9a-f]/gi,"");if(origHex.length<6){origHex=origHex[0]+origHex[0]+origHex[1]+origHex[1]+origHex[2]+origHex[2]}lum=lum||0;var newHex="#",c,i;for(i=0;i<3;i++){c=parseInt(origHex.substr(i*2,2),16);c=Math.round(Math.min(Math.max(0,c+c*lum),255)).toString(16);newHex+=("00"+c).substr(c.length)}result[index]=newHex});return result}};d3.ez.component.barGrouped=function module(){var height=100;var width=300;var colorScale=undefined;var xScale=undefined;var yScale=undefined;var transition={ease:d3.easeBounce,duration:500};var dispatch=d3.dispatch("customMouseOver","customMouseOut","customClick");function my(selection){selection.each(function(data){var barW=xScale.bandwidth();selection.selectAll(".barGrouped").data(function(d){return[d]}).enter().append("g").classed("barGrouped",true).attr("width",width).attr("height",height).on("click",function(d){dispatch.call("customClick",this,d)});var barGroup=selection.selectAll(".barGrouped");var bars=barGroup.selectAll(".bar").data(function(d){return d.values});bars.enter().append("rect").classed("bar",true).attr("fill",function(d){return colorScale(d.key)}).attr("width",barW).attr("x",function(d,i){return xScale(d.key)}).attr("y",height).attr("rx",0).attr("ry",0).attr("height",0).on("mouseover",function(d){dispatch.call("customMouseOver",this,d)}).merge(bars).transition().ease(transition.ease).duration(transition.duration).attr("x",function(d,i){return xScale(d.key)}).attr("y",function(d,i){return yScale(d.value)}).attr("height",function(d,i){return height-yScale(d.value)});bars.exit().transition().style("opacity",0).remove()})}my.height=function(_){if(!arguments.length)return height;height=_;return this};my.width=function(_){if(!arguments.length)return width;width=_;return this};my.colorScale=function(_){if(!arguments.length)return colorScale;colorScale=_;return my};my.xScale=function(_){if(!arguments.length)return xScale;xScale=_;return my};my.yScale=function(_){if(!arguments.length)return yScale;yScale=_;return my};my.dispatch=function(_){if(!arguments.length)return dispatch();dispatch=_;return this};my.on=function(){var value=dispatch.on.apply(dispatch,arguments);return value===dispatch?my:value};return my};d3.ez.component.barStacked=function module(){var height=100;var width=50;var colorScale=undefined;var xScale=undefined;var yScale=undefined;var transition={ease:d3.easeBounce,duration:500};var dispatch=d3.dispatch("customMouseOver","customMouseOut","customClick");function my(selection){selection.each(function(data){selection.selectAll(".barStacked").data(function(d){series=[];var y0=0;d3.map(d.values).values().forEach(function(d,i){series[i]={name:d.key,value:d.value,y0:y0,y1:y0+d.value};y0+=d.value});data={key:data.key,values:series};return[data]}).enter().append("g").classed("barStacked",true).attr("width",width).attr("height",height).on("click",function(d){dispatch.call("customClick",this,d)});var barGroup=selection.selectAll(".barStacked");var bars=barGroup.selectAll(".bar").data(function(d){return d.values});bars.enter().append("rect").classed("bar",true).attr("width",width).attr("x",0).attr("y",height).attr("rx",0).attr("ry",0).attr("height",0).attr("fill",function(d){return colorScale(d.name)}).on("mouseover",function(d){dispatch.call("customMouseOver",this,d)}).merge(bars).transition().ease(transition.ease).duration(transition.duration).attr("width",width).attr("x",0).attr("y",function(d){return yScale(d.y1)}).attr("height",function(d){return yScale(d.y0)-yScale(d.y1)});bars.exit().transition().style("opacity",0).remove()})}my.height=function(_){if(!arguments.length)return height;height=_;return this};my.width=function(_){if(!arguments.length)return width;width=_;return this};my.colorScale=function(_){if(!arguments.length)return colorScale;colorScale=_;return my};my.xScale=function(_){if(!arguments.length)return xScale;xScale=_;return my};my.yScale=function(_){if(!arguments.length)return yScale;yScale=_;return my};my.dispatch=function(_){if(!arguments.length)return dispatch();dispatch=_;return this};my.on=function(){var value=dispatch.on.apply(dispatch,arguments);return value===dispatch?my:value};return my};d3.ez.component.barRadial=function module(){var width=400;var height=300;var colorScale=undefined;var yScale=undefined;var transition={ease:d3.easeBounce,duration:500};var dispatch=d3.dispatch("customMouseOver","customMouseOut","customClick");var radius=undefined;var capitalizeLabels=false;var colorLabels=false;function my(selection){selection.each(function(data){var defaultRadius=Math.min(width,height)/2;radius=typeof radius==="undefined"?defaultRadius:radius;var yDomain=yScale.domain();yDomain[1]=yDomain[1]*1.05;var barScale=d3.scaleLinear().domain(yDomain).range([0,radius]);var axisScale=d3.scaleLinear().domain(yDomain).range([0,-radius]);var arc=d3.arc().innerRadius(0).outerRadius(function(d,i){return barScale(d.value)}).startAngle(function(d,i,j){numBars=j.length;return i*2*Math.PI/numBars}).endAngle(function(d,i,j){numBars=j.length;return(i+1)*2*Math.PI/numBars});var barRadial=selection.selectAll(".barRadial").data(function(d){return[d]}).enter().append("g").classed("barRadial",true).on("click",function(d){dispatch.call("customClick",this,d)});var barRadial=selection.selectAll(".barRadial").merge(barRadial);var segments=barRadial.selectAll("path").data(function(d){return d.values});segments.enter().append("path").style("fill",function(d,i){return colorScale(d.key)}).classed("segment",true).on("mouseover",function(d){dispatch.call("customMouseOver",this,d)}).merge(segments).transition().ease(transition.ease).duration(transition.duration).attr("d",arc);segments.exit().remove()})}my.height=function(_){if(!arguments.length)return height;height=_;return this};my.width=function(_){if(!arguments.length)return width;width=_;return this};my.radius=function(_){if(!arguments.length)return radius;radius=_;return this};my.colorScale=function(_){if(!arguments.length)return colorScale;colorScale=_;return my};my.yScale=function(_){if(!arguments.length)return yScale;yScale=_;return my};my.dispatch=function(_){if(!arguments.length)return dispatch();dispatch=_;return this};my.on=function(){var value=dispatch.on.apply(dispatch,arguments);return value===dispatch?my:value};return my};d3.ez.component.donut=function module(){var height=100;var width=300;var colorScale=undefined;var transition={ease:d3.easeBounce,duration:500};var dispatch=d3.dispatch("customMouseOver","customMouseOut","customClick");var radius=undefined;var innerRadius=undefined;function my(selection){selection.each(function(data){var defaultRadius=Math.min(width,height)/2;radius=typeof radius==="undefined"?defaultRadius:radius;innerRadius=typeof innerRadius==="undefined"?defaultRadius/2:innerRadius;var pie=d3.pie().value(function(d){return d.value}).sort(null);var arc=d3.arc().innerRadius(innerRadius).outerRadius(radius).cornerRadius(3).padAngle(.015);var outerArc=d3.arc().innerRadius(radius*.9).outerRadius(radius*.9);function arcTween(d){var i=d3.interpolate(this._current,d);this._current=i(0);return function(t){return arc(i(t))}}function midAngle(d){return d.startAngle+(d.endAngle-d.startAngle)/2}var chartDonut=selection.selectAll(".donut").data(function(d){return[d]}).enter().append("g").classed("donut",true).on("click",function(d){dispatch.call("customClick",this,d)});chartDonut.append("g").attr("class","slices");chartDonut.append("g").attr("class","labels");chartDonut.append("g").attr("class","lines");var chartDonut=selection.selectAll(".donut");var slices=chartDonut.select(".slices").selectAll("path.slice").data(function(d){return pie(d.values)});slices.enter().append("path").attr("class","slice").attr("fill",function(d){return colorScale(d.data.key)}).attr("d",arc).on("mouseover",function(d){dispatch.call("customMouseOver",this,d)}).merge(slices).transition().duration(transition.duration).ease(transition.ease).attrTween("d",arcTween);slices.exit().remove();var labels=chartDonut.select(".labels").selectAll("text.label").data(function(d){return pie(d.values)});labels.enter().append("text").attr("class","label").attr("dy",".35em").merge(labels).transition().duration(transition.duration).text(function(d,i){return d.data.key}).attrTween("transform",function(d){this._current=this._current||d;var interpolate=d3.interpolate(this._current,d);this._current=interpolate(0);return function(t){var d2=interpolate(t);var pos=outerArc.centroid(d2);pos[0]=radius*(midAngle(d2) "+min:min+" - "+max;return rangeStr;break}var rangeIncrement=domainSize/rangeLength;var ranges=[];var range=[];var rangeStart=domainMin;var rangeEnd=domainMin+rangeIncrement;for(i=0;i-1){ret.splice(ret.indexOf(arrs[i][j-1])+1,0,currEl)}else{ret.push(currEl)}}}}return ret};var categoryNames=function(){if(1==levels){var ret=d3.values(data)[1].map(function(d){return d.key})}else{var ret=[];d3.map(data).values().forEach(function(d,i){var groupName=d.key;d.values.forEach(function(d,i){categoryName=d.key;ret[i]=categoryName});ret=union(ret)})}return ret}();var categoryTotal=function(){if(1==levels){var ret=d3.sum(data.values,function(d){return d.value})}else{var ret=undefined}return ret}();var categoryTotals=function(){if(1==levels){var ret=undefined}else{var ret={};d3.map(data).values().forEach(function(d,i){var groupName=d.key;d.values.forEach(function(d,i){var categoryName=d.key;var categoryValue=+d.value;ret[categoryName]=typeof ret[categoryName]==="undefined"?0:ret[categoryName];ret[categoryName]+=categoryValue})})}return ret}();var categoryTotalsMax=function(){if(1==levels){var ret=undefined}else{var ret=d3.max(d3.values(categoryTotals))}return ret}();var minValue=function(){if(1==levels){var ret=d3.min(data.values,function(d){return d.value})}else{var ret=undefined;d3.map(data).values().forEach(function(d,i){d.values.forEach(function(d,i){ret=typeof ret==="undefined"?d.value:d3.min([ret,d.value])})})}return+ret}();var maxValue=function(){if(1==levels){var ret=d3.max(data.values,function(d){return d.value})}else{var ret=undefined;d3.map(data).values().forEach(function(d,i){d.values.forEach(function(d,i){ret=typeof ret==="undefined"?d.value:d3.max([ret,d.value])})})}return+ret}();var decimalPlaces=function(num){var match=(""+num).match(/(?:\.(\d+))?(?:[eE]([+-]?\d+))?$/);if(!match){return 0}ret=Math.max(0,(match[1]?match[1].length:0)-(match[2]?+match[2]:0));return ret};var maxDecimalPlace=function(){if(1==levels){var ret=undefined}else{var ret=0;d3.map(data).values().forEach(function(d){d.values.forEach(function(d){ret=d3.max([ret,decimalPlaces(d.value)])})})}return ret}();var thresholds=function(){var distance=maxValue-minValue;var ret=[(minValue+.15*distance).toFixed(maxDecimalPlace),(minValue+.4*distance).toFixed(maxDecimalPlace),(minValue+.55*distance).toFixed(maxDecimalPlace),(minValue+.9*distance).toFixed(maxDecimalPlace)];return ret}();var my={levels:levels,groupName:groupName,groupNames:groupNames,groupTotals:groupTotals,groupTotalsMax:groupTotalsMax,categoryNames:categoryNames,categoryTotal:categoryTotal,categoryTotals:categoryTotals,categoryTotalsMax:categoryTotalsMax,minValue:minValue,maxValue:maxValue,maxDecimalPlace:maxDecimalPlace,thresholds:thresholds};return my};d3.ez.chart=function module(){var svg;var canvas;var width=600;var height=400;var margin={top:15,right:15,bottom:15,left:15};var canvasW=580;var canvasH=380;var chartTop=0;var classed="d3ez";var chart=undefined;var legend=undefined;var title=undefined;var creditTag=d3.ez.component.creditTag();var description="";var yAxisLabel="";var colorScale=undefined;var dispatch=d3.dispatch("customMouseOver","customMouseOut","customClick");function init(data){canvasW=width-(margin.left+margin.right);canvasH=height-(margin.top+margin.bottom);chart.dispatch(dispatch).width(canvasW).height(canvasH);if(legend){legend.width(150).height(200);chart.width(chart.width()-legend.width())}if(title){chartTop=title.height();chart.height(chart.height()-title.height())}creditTag.text("d3-ez.net").href("http://d3-ez.net")}function my(selection){selection.each(function(data){init(data);if(!svg){svg=d3.select(this).append("svg").classed(classed,true).attr("width",width).attr("height",height);canvas=svg.append("g").classed("canvas",true);canvas.append("g").classed("chartbox",true);canvas.append("g").classed("legendbox",true);canvas.append("g").classed("titlebox",true);canvas.append("g").classed("creditbox",true)}else{canvas=svg.select(".canvas")}canvas.attr("transform","translate("+margin.left+","+margin.top+")").attr("width",canvasW).attr("height",canvasH);canvas.select(".chartbox").datum(data).attr("transform","translate("+0+","+chartTop+")").call(chart);if(legend&&(typeof chart.colorScale==="function"||typeof chart.sizeScale==="function")){if(typeof chart.colorScale==="function"){legend.colorScale(chart.colorScale())}if(typeof chart.sizeScale==="function"){legend.sizeScale(chart.sizeScale())}canvas.select(".legendbox").attr("transform","translate("+(canvasW-legend.width())+","+title.height()+")").call(legend)}if(title){canvas.select(".titlebox").attr("transform","translate("+width/2+","+0+")").call(title)}canvas.select(".creditbox").attr("transform","translate("+(width-20)+","+(height-20)+")").call(creditTag)})}my.width=function(_){if(!arguments.length)return width;width=_;return this};my.height=function(_){if(!arguments.length)return height;height=_;return this};my.chart=function(_){if(!arguments.length)return chart;chart=_;return this};my.legend=function(_){if(!arguments.length)return legend;legend=_;return this};my.title=function(_){if(!arguments.length)return title;if(typeof _==="string"){title=d3.ez.title().mainText(_).subText("")}else{title=_}return this};my.yAxisLabel=function(_){if(!arguments.length)return yAxisLabel;yAxisLabel=_;return this};my.on=function(){var value=dispatch.on.apply(dispatch,arguments);return value===dispatch?my:value};return my};d3.ez.colors={categorical:function(scheme){switch(scheme){case 1:return["#5da5da","#faa43a","#60bd68","#f17cb0","#b2912f","#b276b2","#decf3f","#f15854","#4d4d4d"];case 2:return["#fbb4ae","#b3cde3","#ccebc5","#decbe4","#fed9a6","#ffffcc","#e5d8bd","#fddaec","#f2f2f2"];case 3:return["#3f51b5","#ff9800","#8bc34a","#9c27b0","#ffeb3b","#03a9f4","#f44336","#009688","#795548"];case 4:return d3.ez.colors.lumShift(d3.ez.colors.lumShift(d3.ez.colors.categorical(3),-.8),5.5)}},diverging:function(scheme){switch(scheme){case 1:return["#8c510a","#bf812d","#dfc27d","#f6e8c3","#f5f5f5","#c7eae5","#80cdc1","#35978f","#01665e"];case 2:return["#d73027","#f46d43","#fdae61","#fee08b","#ffffbf","#d9ef8b","#a6d96a","#66bd63","#1a9850"];case 3:return["#0000ff","#8052fe","#b58bfb","#ddc5f7","#fffff0","#ffcfb4","#ff9e7a","#ff6842","#ff0000"]}},sequential:function(origHex,count){var lumStep=.1;var lumMax=lumStep*count/2;var lumMin=0-lumMax;var lumScale=d3.scale.linear().domain([1,count]).range([lumMin,lumMax]);var result=[];for(var i=1;i<=count;i++){lum=lumScale(i);origHex=String(origHex).replace(/[^0-9a-f]/gi,"");if(origHex.length<6){origHex=origHex[0]+origHex[0]+origHex[1]+origHex[1]+origHex[2]+origHex[2]}var newHex="#";var c;for(var j=0;j<3;j++){c=parseInt(origHex.substr(j*2,2),16);c=Math.round(Math.min(Math.max(0,c+c*lum),255)).toString(16);newHex+=("00"+c).substr(c.length)}result.push(newHex)}return result},lumShift:function(colors,lum){var result=[];colors.forEach(function addNumber(origHex,index){origHex=String(origHex).replace(/[^0-9a-f]/gi,"");if(origHex.length<6){origHex=origHex[0]+origHex[0]+origHex[1]+origHex[1]+origHex[2]+origHex[2]}lum=lum||0;var newHex="#",c,i;for(i=0;i<3;i++){c=parseInt(origHex.substr(i*2,2),16);c=Math.round(Math.min(Math.max(0,c+c*lum),255)).toString(16);newHex+=("00"+c).substr(c.length)}result[index]=newHex});return result}};d3.ez.component.barGrouped=function module(){var height=100;var width=300;var colorScale=undefined;var xScale=undefined;var yScale=undefined;var transition={ease:d3.easeBounce,duration:500};var dispatch=d3.dispatch("customMouseOver","customMouseOut","customClick");function my(selection){selection.each(function(data){var barW=xScale.bandwidth();selection.selectAll(".barGrouped").data(function(d){return[d]}).enter().append("g").classed("barGrouped",true).attr("width",width).attr("height",height).on("click",function(d){dispatch.call("customClick",this,d)});var barGroup=selection.selectAll(".barGrouped");var bars=barGroup.selectAll(".bar").data(function(d){return d.values});bars.enter().append("rect").classed("bar",true).attr("fill",function(d){return colorScale(d.key)}).attr("width",barW).attr("x",function(d,i){return xScale(d.key)}).attr("y",height).attr("rx",0).attr("ry",0).attr("height",0).on("mouseover",function(d){dispatch.call("customMouseOver",this,d)}).merge(bars).transition().ease(transition.ease).duration(transition.duration).attr("x",function(d,i){return xScale(d.key)}).attr("y",function(d,i){return yScale(d.value)}).attr("height",function(d,i){return height-yScale(d.value)});bars.exit().transition().style("opacity",0).remove()})}my.height=function(_){if(!arguments.length)return height;height=_;return this};my.width=function(_){if(!arguments.length)return width;width=_;return this};my.colorScale=function(_){if(!arguments.length)return colorScale;colorScale=_;return my};my.xScale=function(_){if(!arguments.length)return xScale;xScale=_;return my};my.yScale=function(_){if(!arguments.length)return yScale;yScale=_;return my};my.dispatch=function(_){if(!arguments.length)return dispatch();dispatch=_;return this};my.on=function(){var value=dispatch.on.apply(dispatch,arguments);return value===dispatch?my:value};return my};d3.ez.component.barStacked=function module(){var height=100;var width=50;var colorScale=undefined;var xScale=undefined;var yScale=undefined;var transition={ease:d3.easeBounce,duration:500};var dispatch=d3.dispatch("customMouseOver","customMouseOut","customClick");function my(selection){selection.each(function(data){selection.selectAll(".barStacked").data(function(d){series=[];var y0=0;d3.map(d.values).values().forEach(function(d,i){series[i]={name:d.key,value:d.value,y0:y0,y1:y0+d.value};y0+=d.value});data={key:data.key,values:series};return[data]}).enter().append("g").classed("barStacked",true).attr("width",width).attr("height",height).on("click",function(d){dispatch.call("customClick",this,d)});var barGroup=selection.selectAll(".barStacked");var bars=barGroup.selectAll(".bar").data(function(d){return d.values});bars.enter().append("rect").classed("bar",true).attr("width",width).attr("x",0).attr("y",height).attr("rx",0).attr("ry",0).attr("height",0).attr("fill",function(d){return colorScale(d.name)}).on("mouseover",function(d){dispatch.call("customMouseOver",this,d)}).merge(bars).transition().ease(transition.ease).duration(transition.duration).attr("width",width).attr("x",0).attr("y",function(d){return yScale(d.y1)}).attr("height",function(d){return yScale(d.y0)-yScale(d.y1)});bars.exit().transition().style("opacity",0).remove()})}my.height=function(_){if(!arguments.length)return height;height=_;return this};my.width=function(_){if(!arguments.length)return width;width=_;return this};my.colorScale=function(_){if(!arguments.length)return colorScale;colorScale=_;return my};my.xScale=function(_){if(!arguments.length)return xScale;xScale=_;return my};my.yScale=function(_){if(!arguments.length)return yScale;yScale=_;return my};my.dispatch=function(_){if(!arguments.length)return dispatch();dispatch=_;return this};my.on=function(){var value=dispatch.on.apply(dispatch,arguments);return value===dispatch?my:value};return my};d3.ez.component.barRadial=function module(){var width=400;var height=300;var colorScale=undefined;var yScale=undefined;var transition={ease:d3.easeBounce,duration:500};var dispatch=d3.dispatch("customMouseOver","customMouseOut","customClick");var radius=undefined;var capitalizeLabels=false;var colorLabels=false;function my(selection){selection.each(function(data){var defaultRadius=Math.min(width,height)/2;radius=typeof radius==="undefined"?defaultRadius:radius;var yDomain=yScale.domain();yDomain[1]=yDomain[1]*1.05;var barScale=d3.scaleLinear().domain(yDomain).range([0,radius]);var axisScale=d3.scaleLinear().domain(yDomain).range([0,-radius]);var arc=d3.arc().innerRadius(0).outerRadius(function(d,i){return barScale(d.value)}).startAngle(function(d,i,j){numBars=j.length;return i*2*Math.PI/numBars}).endAngle(function(d,i,j){numBars=j.length;return(i+1)*2*Math.PI/numBars});var barRadial=selection.selectAll(".barRadial").data(function(d){return[d]}).enter().append("g").classed("barRadial",true).on("click",function(d){dispatch.call("customClick",this,d)});var barRadial=selection.selectAll(".barRadial").merge(barRadial);var segments=barRadial.selectAll("path").data(function(d){return d.values});segments.enter().append("path").style("fill",function(d,i){return colorScale(d.key)}).classed("segment",true).on("mouseover",function(d){dispatch.call("customMouseOver",this,d)}).merge(segments).transition().ease(transition.ease).duration(transition.duration).attr("d",arc);segments.exit().remove()})}my.height=function(_){if(!arguments.length)return height;height=_;return this};my.width=function(_){if(!arguments.length)return width;width=_;return this};my.radius=function(_){if(!arguments.length)return radius;radius=_;return this};my.colorScale=function(_){if(!arguments.length)return colorScale;colorScale=_;return my};my.yScale=function(_){if(!arguments.length)return yScale;yScale=_;return my};my.dispatch=function(_){if(!arguments.length)return dispatch();dispatch=_;return this};my.on=function(){var value=dispatch.on.apply(dispatch,arguments);return value===dispatch?my:value};return my};d3.ez.component.donut=function module(){var height=100;var width=300;var colorScale=undefined;var transition={ease:d3.easeBounce,duration:500};var dispatch=d3.dispatch("customMouseOver","customMouseOut","customClick");var radius=undefined;var innerRadius=undefined;function my(selection){selection.each(function(data){var defaultRadius=Math.min(width,height)/2;radius=typeof radius==="undefined"?defaultRadius:radius;innerRadius=typeof innerRadius==="undefined"?defaultRadius/2:innerRadius;var pie=d3.pie().value(function(d){return d.value}).sort(null).padAngle(.015);var arc=d3.arc().innerRadius(innerRadius).outerRadius(radius).cornerRadius(2);var outerArc=d3.arc().innerRadius(radius*.9).outerRadius(radius*.9);function arcTween(d){var i=d3.interpolate(this._current,d);this._current=i(0);return function(t){return arc(i(t))}}function midAngle(d){return d.startAngle+(d.endAngle-d.startAngle)/2}var chartDonut=selection.selectAll(".donut").data(function(d){return[d]}).enter().append("g").classed("donut",true).on("click",function(d){dispatch.call("customClick",this,d)});chartDonut.append("g").attr("class","slices");chartDonut.append("g").attr("class","labels");chartDonut.append("g").attr("class","lines");var chartDonut=selection.selectAll(".donut");var slices=chartDonut.select(".slices").selectAll("path.slice").data(function(d){return pie(d.values)});slices.enter().append("path").attr("class","slice").attr("fill",function(d){return colorScale(d.data.key)}).attr("d",arc).on("mouseover",function(d){dispatch.call("customMouseOver",this,d)}).merge(slices).transition().duration(transition.duration).ease(transition.ease).attrTween("d",arcTween);slices.exit().remove();var labels=chartDonut.select(".labels").selectAll("text.label").data(function(d){return pie(d.values)});labels.enter().append("text").attr("class","label").attr("dy",".35em").merge(labels).transition().duration(transition.duration).text(function(d,i){return d.data.key}).attrTween("transform",function(d){this._current=this._current||d;var interpolate=d3.interpolate(this._current,d);this._current=interpolate(0);return function(t){var d2=interpolate(t);var pos=outerArc.centroid(d2);pos[0]=radius*(midAngle(d2) "+min:min+" - "+max;return rangeStr;break}var rangeIncrement=domainSize/rangeLength;var ranges=[];var range=[];var rangeStart=domainMin;var rangeEnd=domainMin+rangeIncrement;for(i=0;i - - - - d3-ez : Reusable Components - - - - - - -
- - - - - diff --git a/package.json b/package.json index 05ed77c0..1b61026c 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "d3-ez", - "version": "2.1.7", + "version": "2.1.8", "description": "D3 Easy Reusable Chary Library", "license": "GPL-3.0", "keywords": [ diff --git a/src/chart/circularHeat.js b/src/chart/circularHeat.js index 5fdda863..0aba1549 100644 --- a/src/chart/circularHeat.js +++ b/src/chart/circularHeat.js @@ -1,13 +1,6 @@ /** * Circular Heat Chart * - * @example - * var myChart = d3.ez.chart.circularHeat(); - * d3.select("#chartholder") - * .datum(data) - * .call(myChart); - * - * Credit: Peter Cook http://animateddata.co.uk/ */ d3.ez.chart.circularHeat = function module() { // SVG and Chart containers (Populated by 'my' function) @@ -15,27 +8,31 @@ d3.ez.chart.circularHeat = function module() { var chart; // Default Options (Configurable via setters) + var classed = "chartCircularHeat"; var width = 400; var height = 300; var margin = { top: 20, right: 20, bottom: 20, left: 20 }; var transition = { ease: d3.easeBounce, duration: 500 }; - var classed = "chartCircularHeat"; var colors = [d3.rgb(214, 245, 0), d3.rgb(255, 166, 0), d3.rgb(255, 97, 0), d3.rgb(200, 65, 65)]; - var radius = undefined; - var innerRadius = undefined; - // Data Options (Populated by 'init' function) + // Chart Dimensions + var chartW; + var chartH; + var radius; + var innerRadius; + + // Scales and Axis + var xScale + var yScale; + var yScale2; + var colorScale; + + // Data Variables + var categoryNames = []; + var groupNames = []; var minValue = 0; var maxValue = 0; - var radialLabels = []; - var numRadials = 24; - var segmentLabels = []; - var numSegments = 24; - var segmentHeight = 0; - - var colorScale = undefined; var thresholds = undefined; - var categoryNames = []; // Dispatch (Custom events) var dispatch = d3.dispatch("customMouseOver", "customMouseOut", "customClick"); @@ -44,9 +41,16 @@ d3.ez.chart.circularHeat = function module() { chartW = width - (margin.left + margin.right); chartH = height - (margin.top + margin.bottom); + var defaultRadius = Math.min(chartW, chartH) / 2; + radius = (typeof radius === 'undefined') ? defaultRadius : radius; + innerRadius = (typeof innerRadius === 'undefined') ? defaultRadius / 4 : innerRadius; + // Slice Data, calculate totals, max etc. var slicedData = d3.ez.dataParse(data); + maxValue = slicedData.maxValue; + minValue = slicedData.minValue; categoryNames = slicedData.categoryNames; + groupNames = slicedData.groupNames; // If thresholds values are not already set // attempt to auto-calculate some thresholds. @@ -55,9 +59,29 @@ d3.ez.chart.circularHeat = function module() { } // Colour Scale - colorScale = d3.scaleThreshold() - .domain(thresholds) - .range(colors); + if (!colorScale) { + // If the colorScale has not already been passed + // then attempt to calculate. + colorScale = d3.scaleThreshold() + .range(colors) + .domain(thresholds); + } + + // X & Y Scales + xScale = d3.scaleBand() + .domain(categoryNames) + .rangeRound([0, chartW]) + .padding(0.1); + + yScale = d3.scaleBand() + .domain(groupNames) + .rangeRound([radius, innerRadius]) + .padding(0.1); + + yScale2 = d3.scaleBand() + .domain(groupNames) + .rangeRound([-innerRadius, -radius]) + .padding(0.1); } function my(selection) { @@ -81,26 +105,36 @@ d3.ez.chart.circularHeat = function module() { .attr("height", height); chart = svg.append("g").classed("chart", true); + chart.append("g").classed("circleRings", true); + chart.append("g").classed("circleLabels", true); + chart.append("g").classed("axis", true); } else { chart = svg.select(".chart"); } // Update the chart dimensions - chart.classed("chartCircularHeat", true) + chart.classed(classed, true) .attr("transform", "translate(" + (width / 2) + "," + (height / 2) + ")") .attr("width", chartW) .attr("height", chartH); - var heatMap = d3.ez.component.heatCircle() - .width(chartW) - .height(chartH) + var heatRing = d3.ez.component.heatRing() + .radius(function(d) { return yScale(d.key) }) + .innerRadius(function(d) { return yScale(d.key) + yScale.bandwidth(); }) .colorScale(colorScale) - .radius(radius) - .innerRadius(innerRadius) + .yScale(yScale) + .xScale(xScale) .dispatch(dispatch); - chart.datum(data) - .call(heatMap); + var series = chart.select(".circleRings").selectAll(".series") + .data(function(d) { return d; }) + .enter().append("g") + .attr("class", "series"); + + series.datum(function(d) { return d; }) + .call(heatRing); + + series.exit().remove(); // Circular Labels var circularLabels = d3.ez.component.circularLabels() @@ -108,9 +142,15 @@ d3.ez.chart.circularHeat = function module() { .height(chartH) .radius(radius); - chart.datum(categoryNames) + chart.select(".circleLabels") + .datum(categoryNames) .call(circularLabels); + // Y Axis + var yAxis = d3.axisLeft(yScale2); + chart.select(".axis") + .call(yAxis); + }); } diff --git a/src/chart/discreteBar.js b/src/chart/discreteBar.js index 60df827f..c22b32fd 100644 --- a/src/chart/discreteBar.js +++ b/src/chart/discreteBar.js @@ -1,15 +1,6 @@ /** * Discrete Bar Chart * - * @example - * var myChart = d3.ez.chart.discreteBar() - * .width(400) - * .height(300) - * .transition({ease: "bounce", duration: 1500}) - * .colors(d3.scaleCategory10().range()); - * d3.select("#chartholder") - * .datum(data) - * .call(myChart); */ d3.ez.chart.discreteBar = function module() { // SVG and Chart containers (Populated by 'my' function) @@ -17,35 +8,47 @@ d3.ez.chart.discreteBar = function module() { var chart; // Default Options (Configurable via setters) + var classed = "chartDiscreteBar"; var width = 400; var height = 300; var margin = { top: 20, right: 20, bottom: 20, left: 40 }; var transition = { ease: d3.easeBounce, duration: 500 }; var colors = d3.ez.colors.categorical(4); - var gap = 0; - - // Data Options (Populated by 'init' function) - var chartW = 0; - var chartH = 0; - var xScale = undefined; - var yScale = undefined; - var xAxis = undefined; - var yAxis = undefined; - var colorScale = undefined; - var yAxisLabel = undefined; + + // Chart Dimensions + var chartW; + var chartH; + + // Scales and Axis + var xScale; + var yScale; + var xAxis; + var yAxis; + var colorScale; // Dispatch (Custom events) var dispatch = d3.dispatch("customMouseOver", "customMouseOut", "customClick"); + // Other Customisation Options + var yAxisLabel = undefined; + function init(data) { chartW = width - (margin.left + margin.right); chartH = height - (margin.top + margin.bottom); // Slice Data, calculate totals, max etc. var slicedData = d3.ez.dataParse(data); - var categoryNames = slicedData.categoryNames; - var maxValue = slicedData.maxValue; - var yAxisLabel = slicedData.groupName; + categoryNames = slicedData.categoryNames; + maxValue = slicedData.maxValue; + yAxisLabel = slicedData.groupName; + + if (!colorScale) { + // If the colorScale has not already been passed + // then attempt to calculate. + colorScale = d3.scaleOrdinal() + .range(colors) + .domain(categoryNames); + } // X & Y Scales xScale = d3.scaleBand() @@ -60,14 +63,6 @@ d3.ez.chart.discreteBar = function module() { // X & Y Axis xAxis = d3.axisBottom(xScale); yAxis = d3.axisLeft(yScale); - - if (!colorScale) { - // If the colorScale has not already been passed - // then attempt to calculate. - colorScale = d3.scaleOrdinal() - .range(colors) - .domain(categoryNames); - } } function my(selection) { @@ -98,7 +93,7 @@ d3.ez.chart.discreteBar = function module() { } // Update the chart dimensions - chart.classed("chartDiscreteBar", true) + chart.classed(classed, true) .attr("transform", "translate(" + margin.left + "," + margin.top + ")") .attr("width", chartW) .attr("height", chartH); diff --git a/src/chart/donut.js b/src/chart/donut.js index faa10fe2..c65b1754 100644 --- a/src/chart/donut.js +++ b/src/chart/donut.js @@ -1,15 +1,6 @@ /** * Donut Chart * - * @example - * var myChart = d3.ez.chart.donut() - * .width(400) - * .height(300) - * .radius(200) - * .innerRadius(50); - * d3.select("#chartholder") - * .datum(data) - * .call(myChart); */ d3.ez.chart.donut = function module() { // SVG and Chart containers (Populated by 'my' function) @@ -17,18 +8,24 @@ d3.ez.chart.donut = function module() { var chart; // Default Options (Configurable via setters) + var classed = "chartDonut"; var width = 400; var height = 300; var margin = { top: 20, right: 20, bottom: 20, left: 20 }; var transition = { ease: d3.easeCubic, duration: 750 }; var colors = d3.ez.colors.categorical(4); - var radius = undefined; - var innerRadius = undefined; - // Data Options (Populated by 'init' function) - var chartW = 0; - var chartH = 0; - var colorScale = undefined; + // Chart Dimensions + var chartW; + var chartH; + var radius; + var innerRadius; + + // Scales and Axis + var colorScale; + + // Data Variables + var categoryNames = []; // Dispatch (Custom events) var dispatch = d3.dispatch("customMouseOver", "customMouseOut", "customClick"); @@ -37,10 +34,15 @@ d3.ez.chart.donut = function module() { chartW = width - (margin.left + margin.right); chartH = height - (margin.top + margin.bottom); + var defaultRadius = Math.min(chartW, chartH) / 2; + radius = (typeof radius === 'undefined') ? defaultRadius : radius; + innerRadius = (typeof innerRadius === 'undefined') ? defaultRadius / 2 : innerRadius; + // Slice Data, calculate totals, max etc. var slicedData = d3.ez.dataParse(data); - var categoryNames = slicedData.categoryNames; + categoryNames = slicedData.categoryNames; + // Colour Scale if (!colorScale) { // If the colorScale has not already been passed // then attempt to calculate. @@ -76,15 +78,13 @@ d3.ez.chart.donut = function module() { } // Update the chart dimensions - chart.classed("chartDonut", true) + chart.classed(classed, true) .attr("transform", "translate(" + (width / 2) + "," + (height / 2) + ")") .attr("width", chartW) .attr("height", chartH); // Add the chart var donutChart = d3.ez.component.donut() - .width(chartW) - .height(chartH) .radius(radius) .innerRadius(innerRadius) .colorScale(colorScale) diff --git a/src/chart/groupedBar.js b/src/chart/groupedBar.js index 443fa4bc..0cd17157 100644 --- a/src/chart/groupedBar.js +++ b/src/chart/groupedBar.js @@ -1,15 +1,6 @@ /** * Grouped Bar Chart * - * @example - * var myChart = d3.ez.chart.groupedBar() - * .width(400) - * .height(300) - * .transition({ease: "bounce", duration: 1500}) - * .groupType("stacked"); - * d3.select("#chartholder") - * .datum(data) - * .call(myChart); */ d3.ez.chart.groupedBar = function module() { // SVG and Chart containers (Populated by 'my' function) @@ -17,38 +8,57 @@ d3.ez.chart.groupedBar = function module() { var chart; // Default Options (Configurable via setters) + var classed = "chartGroupedBar"; var width = 400; var height = 300; var margin = { top: 20, right: 20, bottom: 20, left: 40 }; var transition = { ease: d3.easeBounce, duration: 500 }; var colors = d3.ez.colors.categorical(4); - var gap = 0; - var yAxisLabel = null; - var groupType = "clustered"; - // Data Options (Populated by 'init' function) - var chartW = 0; - var chartH = 0; - var xScale = undefined; - var xScale2 = undefined; - var yScale = undefined; - var xAxis = undefined; - var yAxis = undefined; - var colorScale = undefined; + // Chart Dimensions + var chartW; + var chartH; + + // Scales and Axis + var xScale; + var xScale2; + var yScale; + var xAxis; + var yAxis; + var colorScale; + + // Data Variables + var groupNames; + var groupTotalsMax; + var maxValue; + var categoryNames; // Dispatch (Custom events) var dispatch = d3.dispatch("customMouseOver", "customMouseOut", "customClick"); + // Other Customisation Options + var yAxisLabel = null; + var groupType = "clustered"; + function init(data) { chartW = width - margin.left - margin.right; chartH = height - margin.top - margin.bottom; // Slice Data, calculate totals, max etc. var slicedData = d3.ez.dataParse(data); - var groupNames = slicedData.groupNames; - var groupTotalsMax = slicedData.groupTotalsMax; - var maxValue = slicedData.maxValue; - var categoryNames = slicedData.categoryNames; + groupNames = slicedData.groupNames; + groupTotalsMax = slicedData.groupTotalsMax; + maxValue = slicedData.maxValue; + categoryNames = slicedData.categoryNames; + + // Colour Scale + if (!colorScale) { + // If the colorScale has not already been passed + // then attempt to calculate. + colorScale = d3.scaleOrdinal() + .range(colors) + .domain(categoryNames); + } // X & Y Scales xScale = d3.scaleBand() @@ -68,11 +78,6 @@ d3.ez.chart.groupedBar = function module() { // X & Y Axis xAxis = d3.axisBottom(xScale); yAxis = d3.axisLeft(yScale); - - // Colour Scale - colorScale = d3.scaleOrdinal() - .range(colors) - .domain(categoryNames); } function my(selection) { @@ -109,7 +114,7 @@ d3.ez.chart.groupedBar = function module() { } // Update the chart dimensions - chart.classed("chartGroupedBar", true) + chart.classed(classed, true) .attr("transform", "translate(" + margin.left + "," + margin.top + ")") .attr("width", chartW) .attr("height", chartH); @@ -173,18 +178,18 @@ d3.ez.chart.groupedBar = function module() { return this; }; - my.yAxisLabel = function(_) { - if (!arguments.length) return yAxisLabel; - yAxisLabel = _; - return this; - }; - my.groupType = function(_) { if (!arguments.length) return groupType; groupType = _; return this; }; + my.yAxisLabel = function(_) { + if (!arguments.length) return yAxisLabel; + yAxisLabel = _; + return this; + }; + my.transition = function(_) { if (!arguments.length) return transition; transition = _; diff --git a/src/chart/multiSeriesLine.js b/src/chart/multiSeriesLine.js index f9386e94..ed01de1d 100644 --- a/src/chart/multiSeriesLine.js +++ b/src/chart/multiSeriesLine.js @@ -1,13 +1,6 @@ /** * Multi Series Line Chart * - * @example - * var myChart = d3.ez.chart.multiSeriesLine() - * .width(400) - * .height(300); - * d3.select("#chartholder") - * .datum(data) - * .call(myChart); */ d3.ez.chart.multiSeriesLine = function module() { // SVG and Chart containers (Populated by 'my' function) @@ -15,33 +8,42 @@ d3.ez.chart.multiSeriesLine = function module() { var chart; // Default Options (Configurable via setters) + var classed = "chartMultiSeriesLine"; var width = 400; var height = 300; var margin = { top: 20, right: 20, bottom: 40, left: 40 }; + var transition = { ease: d3.easeBounce, duration: 500 }; var colors = d3.ez.colors.categorical(3); - var yAxisLabel = null; - var groupType = "clustered"; - // Data Options (Populated by 'init' function) - var chartW = 0; - var chartH = 0; - var xScale = undefined; - var yScale = undefined; - var xAxis = undefined; - var yAxis = undefined; - var colorScale = undefined; + // Chart Dimensions + var chartW; + var chartH; + + // Scales and Axis + var xScale; + var yScale; + var xAxis; + var yAxis; + var colorScale; + + // Data Variables + var maxValue; + var groupNames; // Dispatch (Custom events) var dispatch = d3.dispatch("customMouseOver", "customMouseOut", "customClick"); + // Other Customisation Options + var yAxisLabel = null; + function init(data) { chartW = width - margin.left - margin.right; chartH = height - margin.top - margin.bottom; // Slice Data, calculate totals, max etc. var slicedData = d3.ez.dataParse(data); - var maxValue = slicedData.maxValue; - var seriesNames = slicedData.groupNames; + maxValue = slicedData.maxValue; + groupNames = slicedData.groupNames; // Convert dates data.forEach(function(d, i) { @@ -51,6 +53,15 @@ d3.ez.chart.multiSeriesLine = function module() { }); dateDomain = d3.extent(data[0].values, function(d) { return d.key; }); + // Colour Scale + if (!colorScale) { + // If the colorScale has not already been passed + // then attempt to calculate. + colorScale = d3.scaleOrdinal() + .range(colors) + .domain(groupNames); + } + // X & Y Scales xScale = d3.scaleTime() .range([0, chartW]) @@ -64,11 +75,6 @@ d3.ez.chart.multiSeriesLine = function module() { xAxis = d3.axisBottom(xScale) .tickFormat(d3.timeFormat("%d-%b-%y")); yAxis = d3.axisLeft(yScale); - - // Colour Scale - colorScale = d3.scaleOrdinal() - .range(colors) - .domain(seriesNames); } function my(selection) { @@ -105,7 +111,7 @@ d3.ez.chart.multiSeriesLine = function module() { } // Update the chart dimensions - chart.classed("chartMultiSeriesLine", true) + chart.classed(classed, true) .attr("transform", "translate(" + margin.left + "," + margin.top + ")") .attr("width", chartW) .attr("height", chartH); @@ -178,12 +184,6 @@ d3.ez.chart.multiSeriesLine = function module() { return this; }; - my.groupType = function(_) { - if (!arguments.length) return groupType; - groupType = _; - return this; - }; - my.transition = function(_) { if (!arguments.length) return transition; transition = _; diff --git a/src/chart/punchCard.js b/src/chart/punchCard.js index 83d177de..294c3d56 100644 --- a/src/chart/punchCard.js +++ b/src/chart/punchCard.js @@ -1,17 +1,6 @@ /** - * Punchcard + * Punch Card * - * @example - * var myChart = d3.ez.chart.punchCard() - * .width(600) - * .height(350) - * .color("green") - * .minRadius(5) - * .maxRadius(19) - * .useGlobalScale(true); - * d3.select("#chartholder") - * .datum(data) - * .call(myChart); */ d3.ez.chart.punchCard = function module() { // SVG and Chart containers (Populated by 'my' function) @@ -19,45 +8,65 @@ d3.ez.chart.punchCard = function module() { var chart; // Default Options (Configurable via setters) + var classed = "chartPunchCard"; var width = 400; var height = 300; - var margin = { top: 50, right: 40, bottom: 40, left: 40 }; + var margin = { top: 45, right: 20, bottom: 20, left: 45 }; var transition = { ease: d3.easeBounce, duration: 500 }; - var color = "steelblue"; - var sizeScale = undefined; + var colors = [d3.rgb("steelblue").brighter(), d3.rgb("steelblue").darker()]; + + // Chart Dimensions + var chartW; + var chartH; + + // Scales and Axis + var sizeScale; + var xScale; + var yScale; + var xAxis; + var yAxis; + var colorScale; + + // Data Variables + var maxValue; + var minValue; + var categoryNames; + var groupNames; + + // Dispatch (Custom events) + var dispatch = d3.dispatch("customMouseOver", "customMouseOut", "customClick"); + + // Misc Options var minRadius = 2; var maxRadius = 20; var formatTick = d3.format("0000"); var useGlobalScale = true; - // Data Options (Populated by 'init' function) - var chartW = 0; - var chartH = 0; - var xScale = undefined; - var yScale = undefined; - var xAxis = undefined; - var yAxis = undefined; - var colorScale = undefined; - - // Dispatch (Custom events) - var dispatch = d3.dispatch("customMouseOver", "customMouseOut", "customClick"); - function init(data) { chartW = width - margin.left - margin.right; chartH = height - margin.top - margin.bottom; // Slice Data, calculate totals, max etc. var slicedData = d3.ez.dataParse(data); - var maxValue = slicedData.maxValue; - var minValue = slicedData.minValue; - var categoryNames = slicedData.categoryNames; - var groupNames = slicedData.groupNames; + maxValue = slicedData.maxValue; + minValue = slicedData.minValue; + categoryNames = slicedData.categoryNames; + groupNames = slicedData.groupNames; valDomain = [minValue, maxValue]; sizeDomain = useGlobalScale ? valDomain : [0, d3.max(data[1]['values'], function(d) { return d['value']; })]; + // Colour Scale + if (!colorScale) { + // If the colorScale has not already been passed + // then attempt to calculate. + colorScale = d3.scaleLinear() + .domain(valDomain) + .range(colors); + } + // X & Y Scales xScale = d3.scaleBand() .domain(categoryNames) @@ -73,11 +82,6 @@ d3.ez.chart.punchCard = function module() { xAxis = d3.axisTop(xScale); yAxis = d3.axisLeft(yScale); - // Colour Scale - colorScale = d3.scaleLinear() - .domain(valDomain) - .range([d3.rgb(color).brighter(), d3.rgb(color).darker()]); - // Size Scale sizeScale = d3.scaleLinear() .domain(sizeDomain) @@ -86,9 +90,6 @@ d3.ez.chart.punchCard = function module() { function my(selection) { selection.each(function(data) { - // If it is a single object, wrap it in an array - if (data.constructor !== Array) data = [data]; - // Initialise Data init(data); @@ -115,10 +116,10 @@ d3.ez.chart.punchCard = function module() { } // Update the chart dimensions - chart.classed("chartPunchCard", true) + chart.classed(classed, true) .attr("transform", "translate(" + margin.left + "," + margin.top + ")") - .attr("width", width) - .attr("height", height); + .attr("width", chartW) + .attr("height", chartH); // Add axis to chart chart.select(".x-axis") @@ -186,18 +187,18 @@ d3.ez.chart.punchCard = function module() { return this; }; - my.color = function(_) { - if (!arguments.length) return color; - color = _; - return this; - }; - my.sizeScale = function(_) { if (!arguments.length) return sizeScale; sizeScale = _; return this; }; + my.colors = function(_) { + if (!arguments.length) return colors; + colors = _; + return this; + }; + my.useGlobalScale = function(_) { if (!arguments.length) return useGlobalScale; useGlobalScale = _; diff --git a/src/chart/radialBar.js b/src/chart/radialBar.js index fabcf67b..c51e149b 100644 --- a/src/chart/radialBar.js +++ b/src/chart/radialBar.js @@ -1,13 +1,6 @@ /** * Radial Bar Chart * - * @example - * var myChart = d3.ez.chart.radialBar(); - * d3.select("#chartholder") - * .datum(data) - * .call(myChart); - * - * Credit: Peter Cook http://animateddata.co.uk/ */ d3.ez.chart.radialBar = function module() { // SVG and Chart containers (Populated by 'my' function) @@ -15,35 +8,57 @@ d3.ez.chart.radialBar = function module() { var chart; // Default Options (Configurable via setters) + var classed = "chartRadialBar"; var width = 400; var height = 300; var margin = { top: 20, right: 20, bottom: 20, left: 20 }; var transition = { ease: d3.easeBounce, duration: 500 }; var colors = d3.ez.colors.categorical(4); - var radius = undefined; - var capitalizeLabels = false; - var colorLabels = false; - // Data Options (Populated by 'init' function) - var chartW = 0; - var chartH = 0; - var yScale = undefined; - var colorScale = undefined; + // Chart Dimensions + var chartW; + var chartH; + var radius; + var innerRadius; + + // Scales and Axis + var xScale + var yScale; + var yScale2; + var colorScale; + + // Data Variables var categoryNames = []; var maxValue = 0; // Dispatch (Custom events) var dispatch = d3.dispatch("customMouseOver", "customMouseOut", "customClick"); + // Other Customisation Options + var capitalizeLabels = false; + var colorLabels = false; + function init(data) { chartW = width - (margin.left + margin.right); chartH = height - (margin.top + margin.bottom); + var defaultRadius = Math.min(chartW, chartH) / 2; + radius = (typeof radius === 'undefined') ? defaultRadius : radius; + innerRadius = (typeof innerRadius === 'undefined') ? defaultRadius / 4 : innerRadius; + // Slice Data, calculate totals, max etc. var slicedData = d3.ez.dataParse(data); - maxValue = slicedData.maxValue; - var domain = [0, maxValue]; categoryNames = slicedData.categoryNames; + maxValue = slicedData.maxValue; + + // Colour Scale + if (!colorScale) { + // If the colorScale has not already been passed + // then attempt to calculate. + colorScale = d3.scaleOrdinal() + .range(colors) + .domain(categoryNames); + } // X & Y Scales xScale = d3.scaleBand() @@ -52,13 +67,12 @@ d3.ez.chart.radialBar = function module() { .padding(0.15); yScale = d3.scaleLinear() - .domain(domain) + .domain([0, maxValue]) .range([0, radius]); - // Colour Scale - colorScale = d3.scaleOrdinal() - .range(colors) - .domain(categoryNames); + yScale2 = d3.scaleLinear() + .domain([0, maxValue]) + .range([0, -radius]); } function my(selection) { @@ -91,15 +105,13 @@ d3.ez.chart.radialBar = function module() { } // Update the chart dimensions - chart.classed("chartRadialBar", true) + chart.classed(classed, true) .attr("transform", "translate(" + (width / 2) + "," + (height / 2) + ")") - .attr("width", width) - .attr("height", height); + .attr("width", chartW) + .attr("height", chartH); // Add the chart var barRadial = d3.ez.component.barRadial() - .width(chartW) - .height(chartH) .radius(radius) .yScale(yScale) .colorScale(colorScale) @@ -121,7 +133,6 @@ d3.ez.chart.radialBar = function module() { .call(circularAxis); // Y Axis - var yScale2 = d3.scaleLinear().domain(yScale.domain()).range([0, -radius]); var yAxis = d3.axisLeft(yScale2); chart.select(".axis") .call(yAxis); @@ -143,21 +154,18 @@ d3.ez.chart.radialBar = function module() { my.width = function(_) { if (!arguments.length) return width; width = _; - radius = d3.min([(width - (margin.right + margin.left)), (height - (margin.top + margin.bottom))]) / 2; return this; }; my.height = function(_) { if (!arguments.length) return height; height = _; - radius = d3.min([(width - (margin.right + margin.left)), (height - (margin.top + margin.bottom))]) / 2; return this; }; my.margin = function(_) { if (!arguments.length) return margin; margin = _; - radius = d3.min([(width - (margin.right + margin.left)), (height - (margin.top + margin.bottom))]) / 2; return this; }; diff --git a/src/chart/tabularHeat.js b/src/chart/tabularHeat.js index edfd3031..f4a9a051 100644 --- a/src/chart/tabularHeat.js +++ b/src/chart/tabularHeat.js @@ -1,11 +1,6 @@ /** * Tabular Heat Chart * - * @example - * var myChart = d3.ez.chart.tabularHeat(); - * d3.select("#chartholder") - * .datum(data) - * .call(myChart); */ d3.ez.chart.tabularHeat = function module() { // SVG and Chart containers (Populated by 'my' function) @@ -13,20 +8,29 @@ d3.ez.chart.tabularHeat = function module() { var chart; // Default Options (Configurable via setters) - var width = 600; - var height = 600; - var margin = { top: 50, right: 40, bottom: 40, left: 40 }; + var classed = "chartTabularHeat"; + var width = 400; + var height = 300; + var margin = { top: 45, right: 20, bottom: 20, left: 45 }; var transition = { ease: d3.easeBounce, duration: 500 }; var colors = [d3.rgb(214, 245, 0), d3.rgb(255, 166, 0), d3.rgb(255, 97, 0), d3.rgb(200, 65, 65)]; - // Data Options (Populated by 'init' function) - var chartW = 0; - var chartH = 0; - var xScale = undefined; - var yScale = undefined; - var xAxis = undefined; - var yAxis = undefined; - var colorScale = undefined; + // Chart Dimensions + var chartW; + var chartH; + + // Scales and Axis + var xScale; + var yScale; + var colorScale; + var xAxis; + var yAxis; + + // Data Variables + var groupNames = []; + var categoryNames = []; + var minValue = 0; + var maxValue = 0; var thresholds = undefined; // Dispatch (Custom events) @@ -38,10 +42,10 @@ d3.ez.chart.tabularHeat = function module() { // Slice Data, calculate totals, max etc. var slicedData = d3.ez.dataParse(data); - var maxValue = slicedData.maxValue; - var minValue = slicedData.minValue; - var categoryNames = slicedData.categoryNames; - var groupNames = slicedData.groupNames; + maxValue = slicedData.maxValue; + minValue = slicedData.minValue; + categoryNames = slicedData.categoryNames; + groupNames = slicedData.groupNames; // If thresholds values are not already set // attempt to auto-calculate some thresholds. @@ -49,6 +53,15 @@ d3.ez.chart.tabularHeat = function module() { var thresholds = slicedData.thresholds; } + // Colour Scale + if (!colorScale) { + // If the colorScale has not already been passed + // then attempt to calculate. + colorScale = d3.scaleThreshold() + .domain(thresholds) + .range(colors); + } + // X & Y Scales xScale = d3.scaleBand() .domain(categoryNames) @@ -63,11 +76,6 @@ d3.ez.chart.tabularHeat = function module() { // X & Y Axis xAxis = d3.axisTop(xScale); yAxis = d3.axisLeft(yScale); - - // Colour Scale - colorScale = d3.scaleThreshold() - .domain(thresholds) - .range(colors); } function my(selection) { @@ -98,7 +106,7 @@ d3.ez.chart.tabularHeat = function module() { } // Update the chart dimensions - chart.classed("chartTabularHeat", true) + chart.classed(classed, true) .attr("transform", "translate(" + margin.left + "," + margin.top + ")") .attr("width", chartW) .attr("height", chartH); @@ -133,6 +141,9 @@ d3.ez.chart.tabularHeat = function module() { .call(heatMap); series.exit().remove(); + + + }); } @@ -173,12 +184,6 @@ d3.ez.chart.tabularHeat = function module() { return this; }; - my.accessor = function(_) { - if (!arguments.length) return accessor; - accessor = _; - return this; - }; - my.dispatch = function(_) { if (!arguments.length) return dispatch(); dispatch = _; diff --git a/src/component/barStacked.js b/src/component/barStacked.js index 1d5eab46..9e829676 100644 --- a/src/component/barStacked.js +++ b/src/component/barStacked.js @@ -33,7 +33,7 @@ d3.ez.component.barStacked = function module() { y0 += d.value; }); - data = {key: data.key, values: series}; + data = { key: data.key, values: series }; return [data]; }) .enter() diff --git a/src/component/circularLabels.js b/src/component/circularLabels.js index e4271b7a..a355c377 100644 --- a/src/component/circularLabels.js +++ b/src/component/circularLabels.js @@ -17,7 +17,7 @@ d3.ez.component.circularLabels = function module() { selection.each(function(data) { var defaultRadius = Math.min(width, height) / 2; radius = (typeof radius === 'undefined') ? defaultRadius : radius; - var labelRadius = radius * 1.050; + var labelRadius = radius * 1.020; var circularLabels = selection.selectAll('.circularLabels') .data(function(d) { return [d]; }) diff --git a/src/component/donut.js b/src/component/donut.js index 320249be..63a4b2c7 100644 --- a/src/component/donut.js +++ b/src/component/donut.js @@ -22,15 +22,17 @@ d3.ez.component.donut = function module() { radius = (typeof radius === 'undefined') ? defaultRadius : radius; innerRadius = (typeof innerRadius === 'undefined') ? defaultRadius / 2 : innerRadius; + // Pie Generator var pie = d3.pie() .value(function(d) { return d.value; }) - .sort(null); + .sort(null) + .padAngle(0.015); + // Arc Generators var arc = d3.arc() .innerRadius(innerRadius) .outerRadius(radius) - .cornerRadius(3) - .padAngle(0.015); + .cornerRadius(2); var outerArc = d3.arc() .innerRadius(radius * 0.9) diff --git a/src/component/heatCircle.js b/src/component/heatCircle.js deleted file mode 100644 index 8dbf006d..00000000 --- a/src/component/heatCircle.js +++ /dev/null @@ -1,165 +0,0 @@ -/** - * Reusable Circular Heat Map - * - * @example - * var myBars = d3.ez.component.heatCircle() - * .colorScale(**D3 Scale Object**); - * d3.select("svg").call(myBars); - */ -d3.ez.component.heatCircle = function module() { - // Default Options (Configurable via setters) - var width = 400; - var height = 300; - var colorScale = undefined; - var transition = { ease: d3.easeBounce, duration: 500 }; - var dispatch = d3.dispatch("customMouseOver", "customMouseOut", "customClick"); - var radius = undefined; - var innerRadius = undefined; - - function my(selection) { - selection.each(function(data) { - - // Slice Data, calculate totals, max etc. - var slicedData = d3.ez.dataParse(data); - groupNames = slicedData.groupNames; - numRadials = groupNames.length; - categoryNames = slicedData.categoryNames; - numSegments = categoryNames.length; - - var defaultRadius = Math.min(width, height) / 2; - radius = (typeof radius === 'undefined') ? defaultRadius : radius; - innerRadius = (typeof innerRadius === 'undefined') ? defaultRadius / 4 : innerRadius; - var segmentHeight = ((radius - innerRadius) / numRadials); - - // Arc Generator - var arc = d3.arc() - .innerRadius(function(d, i) { - return innerRadius + segmentHeight * d.ring; - }) - .outerRadius(function(d, i) { - return innerRadius + segmentHeight * (d.ring + 1); - }) - .startAngle(function(d, i) { - return (i * 2 * Math.PI) / numSegments; - }) - .endAngle(function(d, i) { - return ((i + 1) * 2 * Math.PI) / numSegments; - }); - - // Create chart group - var circularHeat = selection.selectAll('.heatCircle') - .data(function(d) { return [d]; }) - .enter() - .append("g") - .classed("heatCircle", true) - .on("click", function(d) { dispatch.call("customClick", this, d); }); - - circularHeat.append("g").attr("class", "rings"); - circularHeat.append("g").attr("class", "radialLabels"); - var circularHeat = selection.selectAll('.heatCircle').merge(circularHeat); - - // Rings - circularHeat.select(".rings").selectAll("g") - .data(function(d) { return d; }) - .enter() - .append("g") - .classed("ring", true); - - // Ring Segments - circularHeat.selectAll(".ring").selectAll("path") - .data(function(d, i) { - // Add index (used to calculate ring number) - for (j = 0; j < numSegments; j++) { - d.values[j].ring = i; - } - return d.values; - }) - .enter() - .append("path") - .attr("d", arc) - .attr("fill", function(d) { - return colorScale(d.value); - }) - .classed("segment", true) - .on("mouseover", function(d) { dispatch.call("customMouseOver", this, d); }); - - // Radial Labels - var lsa = 0.01; // Label start angle - var radialLabels = circularHeat.select(".radialLabels") - .classed("labels", true); - - radialLabels.selectAll("def") - .data(function(d) { - return d; - }) - .enter() - .append("def") - .append("path") - .attr("id", function(d, i) { - return "radial-label-path-" + i; - }) - .attr("d", function(d, i) { - var r = innerRadius + ((i + 0.2) * segmentHeight); - return "m" + r * Math.sin(lsa) + " -" + r * Math.cos(lsa) + " a" + r + " " + r + " 0 1 1 -1 0"; - }); - - radialLabels.selectAll("text") - .data(groupNames) - .enter() - .append("text") - .append("textPath") - .attr("xlink:href", function(d, i) { - return "#radial-label-path-" + i; - }) - .text(function(d) { - return d; - }); - - }); - - } - - // Configuration Getters & Setters - my.height = function(_) { - if (!arguments.length) return height; - height = _; - return this; - }; - - my.width = function(_) { - if (!arguments.length) return width; - width = _; - return this; - }; - - my.radius = function(_) { - if (!arguments.length) return radius; - radius = _; - return this; - }; - - my.innerRadius = function(_) { - if (!arguments.length) return innerRadius; - innerRadius = _; - return this; - }; - - my.colorScale = function(_) { - if (!arguments.length) return colorScale; - colorScale = _; - return my; - }; - - my.dispatch = function(_) { - if (!arguments.length) return dispatch(); - dispatch = _; - return this; - }; - - my.on = function() { - var value = dispatch.on.apply(dispatch, arguments); - return value === dispatch ? my : value; - }; - - return my; -}; diff --git a/src/component/heatMap.js b/src/component/heatMap.js index 999ebae9..fb7bccdb 100644 --- a/src/component/heatMap.js +++ b/src/component/heatMap.js @@ -8,8 +8,8 @@ */ d3.ez.component.heatMap = function module() { // Default Options (Configurable via setters) - var height = 100; var width = 300; + var height = 100; var colorScale = undefined; var xScale = undefined; var yScale = undefined; @@ -21,37 +21,37 @@ d3.ez.component.heatMap = function module() { var cellHeight = yScale.bandwidth(); var cellWidth = xScale.bandwidth(); - selection.selectAll('.deck') + var heatMap = selection.selectAll('.heatMap') .data(function(d) { return [d]; }) .enter() .append("g") - .classed('deck', true) + .classed('heatMap', true) .on("click", function(d) { dispatch.call("customClick", this, d); }); - var deck = selection.selectAll('.deck'); + heatMap = selection.selectAll('.heatMap').merge(heatMap); - var cards = deck.selectAll(".card") + var cells = heatMap.selectAll(".cell") .data(function(d) { return d.values; }); - cards.enter().append("rect") + cells.enter().append("rect") .attr("x", function(d, i) { return xScale(d.key); }) .attr("y", 0) - .attr("rx", 3) - .attr("ry", 3) - .attr("class", "card") + .attr("rx", 2) + .attr("ry", 2) + .attr("class", "cell") .attr("width", cellWidth) .attr("height", cellHeight) .on("click", dispatch.customClick) .on("mouseover", function(d) { dispatch.call("customMouseOver", this, d); }) - .merge(cards) + .merge(cells) .transition() .duration(1000) .attr("fill", function(d) { return colorScale(d.value); }); - cards.exit().remove(); + cells.exit().remove(); - cards.select("title").text(function(d) { + cells.select("title").text(function(d) { return d.value; }); diff --git a/src/component/heatRing.js b/src/component/heatRing.js new file mode 100644 index 00000000..7215e012 --- /dev/null +++ b/src/component/heatRing.js @@ -0,0 +1,130 @@ +/** + * Reusable Circular Heat Ring + * + * @example + * var myBars = d3.ez.component.heatRing() + * .colorScale(**D3 Scale Object**); + * d3.select("svg").call(myBars); + */ +d3.ez.component.heatRing = function module() { + // Default Options (Configurable via setters) + var width = 400; + var height = 300; + var colorScale = undefined; + var xScale = undefined; + var yScale = undefined; + var transition = { ease: d3.easeBounce, duration: 500 }; + var dispatch = d3.dispatch("customMouseOver", "customMouseOut", "customClick"); + var radius = undefined; + var innerRadius = undefined; + + function my(selection) { + selection.each(function(data) { + var defaultRadius = Math.min(width, height) / 2; + radius = (typeof radius === 'undefined') ? defaultRadius : radius; + innerRadius = (typeof innerRadius === 'undefined') ? defaultRadius / 4 : innerRadius; + + // Pie Generator + var pie = d3.pie() + .value(function(d) { return 1; }) + .sort(null) + .padAngle(0.015); + + // Arc Generator + var arc = d3.arc() + .outerRadius(radius) + .innerRadius(innerRadius) + .cornerRadius(2); + + // Create chart group + var heatRing = selection.selectAll('.heatRing') + .data(function(d) { return [d]; }) + .enter() + .append("g") + .classed("heatRing", true) + .on("click", function(d) { dispatch.call("customClick", this, d); }); + heatRing = selection.selectAll('.heatRing').merge(heatRing); + + var segments = heatRing.selectAll(".segment") + .data(function(d) { + var key = d.key; + var data = pie(d.values); + data.forEach(function(d, i) { + data[i].key = key; + }); + + return data; + }); + + // Ring Segments + segments + .enter() + .append("path") + .attr("d", arc) + .attr("fill", function(d) { + return colorScale(d.data.value); + }) + .classed("segment", true) + .on("mouseover", function(d) { dispatch.call("customMouseOver", this, d.data); }); + + segments.exit().remove(); + }); + + } + + // Configuration Getters & Setters + my.height = function(_) { + if (!arguments.length) return height; + height = _; + return this; + }; + + my.width = function(_) { + if (!arguments.length) return width; + width = _; + return this; + }; + + my.radius = function(_) { + if (!arguments.length) return radius; + radius = _; + return this; + }; + + my.innerRadius = function(_) { + if (!arguments.length) return innerRadius; + innerRadius = _; + return this; + }; + + my.colorScale = function(_) { + if (!arguments.length) return colorScale; + colorScale = _; + return my; + }; + + my.xScale = function(_) { + if (!arguments.length) return xScale; + xScale = _; + return my; + }; + + my.yScale = function(_) { + if (!arguments.length) return yScale; + yScale = _; + return my; + }; + + my.dispatch = function(_) { + if (!arguments.length) return dispatch(); + dispatch = _; + return this; + }; + + my.on = function() { + var value = dispatch.on.apply(dispatch, arguments); + return value === dispatch ? my : value; + }; + + return my; +}; diff --git a/src/header.js b/src/header.js index af147317..cdb30e9c 100644 --- a/src/header.js +++ b/src/header.js @@ -6,7 +6,7 @@ * @license GPLv3 */ d3.ez = { - version: "2.1.7", + version: "2.1.8", author: "James Saunders", copyright: "Copyright (C) 2017 James Saunders", license: "GPL-3.0" diff --git a/src/vega.js b/src/vega.js deleted file mode 100644 index e979e070..00000000 --- a/src/vega.js +++ /dev/null @@ -1,210 +0,0 @@ -/** - * Vega - * - * @example - * @todo - */ -d3.ez.vega = function module() { - // SVG and Canvas containers (Populated by 'my' function) - var svg; - var canvas; - - // Default Options (Configurable via setters) - var width = 1000; - var height = 600; - var margin = { top: 30, right: 30, bottom: 30, left: 30 }; - var padding = 20; - var canvasW = 580; - var canvasH = 380; - var chartTop = 0; - var classed = "d3ez"; - - var chart = undefined; - var yScale = undefined; - var yAxis = undefined; - var xScale = undefined; - var xAxis = undefined; - var legend = undefined; - var title = undefined; - var creditTag = d3.ez.component.creditTag(); - var yAxisLabel = ""; - - // Colours - var colorScale = undefined; - - // Dispatch (custom events) - var dispatch = d3.dispatch("customMouseOver", "customMouseOut", "customClick"); - - function init(data) { - canvasW = width - (margin.left + margin.right); - canvasH = height - (margin.top + margin.bottom); - - // Init Chart - chart.colorScale(colorScale) - .xScale(xScale) - .yScale(yScale) - .width(canvasW) - .height(canvasH) - .dispatch(dispatch); - - // Init Legend - if (legend) { - legend.width(120).height(200); - chart.width(chart.width() - legend.width() - padding); - } - - // Init Title - if (title) { - chartTop = title.height() + padding; - chart.height(chart.height() - title.height() - padding); - } - - // Have we changed the graph size? - xScale.range([chart.width(), 0]); - yScale.range([chart.height(), 0]); - - // Init Axis - xAxis = d3.axisBottom(xScale); - yAxis = d3.axisLeft(yScale); - - // Init Credit Tag - creditTag.text("d3-ez.net").href("http://d3-ez.net"); - } - - function my(selection) { - selection.each(function(data) { - init(data); - - // Create SVG element (if it does not exist already) - if (!svg) { - svg = d3.select(this) - .append("svg") - .classed(classed, true) - .attr("width", width) - .attr("height", height); - - canvas = svg.append("g").classed("canvas", true); - canvas.append("g").classed("chartbox", true); - canvas.select(".chartbox").append("g").classed("x-axis axis", true); - canvas.select(".chartbox").append("g").classed("y-axis axis", true); - canvas.append("g").classed("legendbox", true); - canvas.append("g").classed("titlebox", true); - svg.append("g").classed("creditbox", true); - } else { - canvas = svg.select(".canvas") - } - - // Update the canvas dimensions - canvas.attr("transform", "translate(" + margin.left + "," + margin.top + ")") - .attr("width", canvasW) - .attr("height", canvasH); - - // Add Chart - canvas.select(".chartbox") - .attr("transform", "translate(0," + chartTop + ")") - .datum(data) - .call(chart); - - canvas.select(".y-axis") - .call(yAxis); - - canvas.select(".x-axis") - .attr("transform", "translate(0," + chart.height() + ")") - .call(xAxis); - - // Add Title - if (title) { - canvas.select(".titlebox") - .attr("transform", "translate(" + width / 2 + "," + 0 + ")") - .call(title); - } - - // Add Legend - if (legend && (typeof chart.colorScale === "function" || typeof chart.sizeScale === "function")) { - if (typeof chart.colorScale === "function") { - legend.colorScale(chart.colorScale()); - } - if (typeof chart.sizeScale === "function") { - legend.sizeScale(chart.sizeScale()); - } - canvas.select(".legendbox") - .attr("transform", "translate(" + (canvasW - legend.width()) + "," + chartTop + ")") - .call(legend); - } - - // Add Credit Tag - if (creditTag) { - svg.select(".creditbox") - .attr("transform", "translate(" + (width - 10) + "," + (height - 5) + ")") - .call(creditTag); - } - }); - } - - // Configuration Getters & Setters - my.width = function(_) { - if (!arguments.length) return width; - width = _; - return this; - }; - - my.height = function(_) { - if (!arguments.length) return height; - height = _; - return this; - }; - - my.chart = function(_) { - if (!arguments.length) return chart; - chart = _; - return this; - }; - - my.colorScale = function(_) { - if (!arguments.length) return colorScale; - colorScale = _; - return this; - }; - - my.xScale = function(_) { - if (!arguments.length) return xScale; - xScale = _; - return this; - }; - - my.yScale = function(_) { - if (!arguments.length) return yScale; - yScale = _; - return this; - }; - - my.legend = function(_) { - if (!arguments.length) return legend; - legend = _; - return this; - }; - - my.title = function(_) { - if (!arguments.length) return title; - if (typeof _ === "string") { - // If the caller has passed a plain string convert it to a title object. - title = d3.ez.title().mainText(_).subText(''); - } else { - title = _; - } - return this; - }; - - my.yAxisLabel = function(_) { - if (!arguments.length) return yAxisLabel; - yAxisLabel = _; - return this; - }; - - my.on = function() { - var value = dispatch.on.apply(dispatch, arguments); - return value === dispatch ? my : value; - } - - return my; -}; diff --git a/test/DataTest.html b/test/DataTest.html index 79a23483..4e2aeb5c 100644 --- a/test/DataTest.html +++ b/test/DataTest.html @@ -4,13 +4,13 @@ d3-ez : Data Parse Test - + diff --git a/test/d3-ez.js b/test/d3-ez.js index 559dcbad..91c8628a 100644 --- a/test/d3-ez.js +++ b/test/d3-ez.js @@ -4,7 +4,6 @@ include('../src/header.js'); include('../src/baseFunctions.js'); include('../src/chart.js'); -include('../src/vega.js'); include('../src/colors.js'); include('../src/component/barGrouped.js'); include('../src/component/barStacked.js'); @@ -15,7 +14,7 @@ include('../src/component/labeledNode.js'); include('../src/component/legend.js'); include('../src/component/lineChart.js'); include('../src/component/heatMap.js'); -include('../src/component/heatCircle.js'); +include('../src/component/heatRing.js'); include('../src/component/punchCard.js'); include('../src/component/numberCard.js'); include('../src/component/scatterPlot.js');