/* [ { "type": "stacked_barchart", "processing": false, "id": "chart", "visible": true, "data": [ { "group": "TD2", "class1": 20, "class2": 20, "class3": 20, "class4": 10, "class5": 10, "class6": 10, "class7": 10 }, { "group": "TD3", "class1": 10, "class2": 10, "class3": 5, "class4": 15, "class5": 20, "class6": 20, "class7": 20 }, { "group": "TD4", "class1": 10, "class2": 10, "class3": 5, "class4": 15, "class5": 20, "class6": 20, "class7": 20 }, { "group": "TD5", "class1": 10, "class2": 10, "class3": 5, "class4": 15, "class5": 20, "class6": 20, "class7": 20 } ], "options": { "title": "연도 비율 비교 차트", "y_name": "Percent %", "name": "keyword", "x_rotate": -90, "bar_weight": 0.5, "space": 0.5, "group_field": "group", "class_fields": [ ], "text_size": 10, "line_thickness": 0.4, "line_dash": 4, "line_color": "black", "line_opacity": 1.0, "vertical_index": 2 } } ] [ { "type": "stacked_barchart", "processing": false, "id": "chart", "visible": true, "data": [ { "group": "3분기", "음식": 35, "숙박": 30, "교통": 16, "기타": 20 }, { "group": "전체기간", "음식": 36, "숙박": 25, "교통": 11, "기타": 17 } ], "options": { "title": "연도 비율 비교 차트", "y_name": "Percent %", "name": "keyword", "x_rotate": -90, "bar_weight": 0.5, "space": 0.5, "group_field": "group", "class_fields": [ ], "text_size": 10, "line_thickness": 0.4, "line_dash": 4, "line_color": "black", "line_opacity": 1.0, "vertical_index": 2 } } ] */ var _stacked = { // 초기값 defaultOptions : { "title": "비율 비교 차트", "group_field": "group", "y_name": "Percent %", "bar_color": ["#B9B9B9", "#E9B8AD", "#EA9C8C", "#900C3F"],//d3.scale.category10().range(), // ["#006622", "#110022"] 색의 종류 보다 class가 많으면 반복 "vertical_index": 0, // 1부터 시작 , 0은 없음 "stroke_width": 0.4, "stroke_color": "#000000", // 바를 연결하는 선들의 색 "x_rotate": 0, "x_text_dy": 1.5, "bar_font_size": 10, "bar_font_color": "#ffffff", "height": 0, // 강제 조정 안 함 //"line_dash": 4, // 바를 연결하는 선들의 색 // // "space": 0.5, // "text_size": 10, // "bar_weight": 0.5, }, // chart data 존재하는지 validateData : function(unit) { console.log(unit) var options = unit.options; if (options == null || Object.keys(options).length == 0) { console.error("options is empty : " + unit.id); // 옵션이 없는 경우 default 옵션을 넣도록 함 options = unit.options = _stacked.defaultOptions; } if (unit.data == null || Object.keys(unit.data).length == 0) { console.error("data is empty : " + unit.id); return false; } // unit.data가 array가 아닌 일반 object인 경우 array로 변경해줌. if (unit.data.forEach == null) { console.error("unit.data is not array(is object), change to array : " + unit.id); unit.data = [unit.data]; } // 누락된 option을 default 값으로 대체 Object.keys(_stacked.defaultOptions).forEach(function(name) { if (!options.hasOwnProperty(name)) { unit.options[name] = _stacked.defaultOptions[name]; } }); return true; }, wrap : function(text, width) { text.each(function() { var text = d3.select(this), words = text.text().split(/\s+/).reverse(), word, line = [], lineNumber = 0, lineHeight = 1.1, // ems y = text.attr("y"), dy = parseFloat(text.attr("dy")), tspan = text.text(null).append("tspan").attr("x", 0).attr("y", y).attr("dy", dy + "em"); while (word = words.pop()) { line.push(word); tspan.text(line.join(" ")); if (tspan.node().getComputedTextLength() > width) { line.pop(); tspan.text(line.join(" ")); line = [word]; tspan = text.append("tspan").attr("x", 0).attr("y", y).attr("dy", ++lineNumber * lineHeight + dy + "em").text(word); } } }); }, // 색상(hex)을 더 밝게/어둡게 조절하기(lum는 -1 ~ 1 사이 값이며, *100% 만큼 밝아지거나 어두워짐) render : function($dom, unit) { var $chartDiv = $("
"); if (unit.options.height) $chartDiv.css({"height": unit.options.height, "min-height": unit.options.height}); $dom.append($chartDiv); var chartDivId = ReportRenderer.getUniqueDivId(); var strChartDivId = '#' + chartDivId; var $innerDiv = $(""); if (unit.options.height) $innerDiv.css({"height": unit.options.height - 30, "min-height": unit.options.height - 30}); $chartDiv.append($innerDiv); if (_stacked.validateData(unit) == false) { $dom.append("stacked_barchart 데이터가 올바르지 않습니다." + JSON.stringify(unit, null, 2) + ""); return; } // Setting margin var margin = { top: (parseInt(d3.select(strChartDivId).style('height'), 10) / 10), right: (parseInt(d3.select(strChartDivId).style('width'), 10) / 20), bottom: (parseInt(d3.select(strChartDivId).style('height'), 10) / 10), left: (parseInt(d3.select(strChartDivId).style('width'), 10) / 6) } //SVG width, height width = parseInt(d3.select(strChartDivId).style('width'), 10) - margin.left - margin.right; height = parseInt(d3.select(strChartDivId).style('height'), 10) - margin.top - margin.bottom; //Extract chart data, options dataset = unit['data'] options = unit['options'] group_field = options['group_field'] class_fields = d3.keys(unit.data[0]).filter(function(key) {return key !== options['group_field'];}) dataset.forEach(function(d) { var y0 = 0; d.values = class_fields.map(function(name) { return {name: name, y0: y0, y1: y0 += +d[name]}; }); d.total = d.values[d.values.length - 1].y1; }); var color = d3.scale.ordinal() .domain(class_fields) .range(options['bar_color']) var x = d3.scale.ordinal() .domain(dataset.map(function(d) {return d[group_field]})) .rangeRoundBands([0, width], .50, .3); var y = d3.scale.linear() .domain([0, d3.max(dataset, function(d) { return d.total })]) .rangeRound([height, 0]); var svg = d3.select('#' + chartDivId).append("svg") .attr("width", width + margin.left + margin.right) .attr("height", height + margin.top + margin.bottom) .append("g") .attr("transform", "translate(" + margin.left + "," + margin.top + ")"); var xAxis = d3.svg.axis() .scale(x) .tickSize(0.6) .orient("bottom") var yAxis = d3.svg.axis() .scale(y) .orient("left") .tickSize(1) .tickFormat(d3.formatPrefix(".1", 1e6)) //.tickFormat(d3.format(".2s")) svg.append("g") .attr("class", "x axis") .attr("transform", "translate(0," + height +")") .call(xAxis) .selectAll("text") .attr("dy", options["x_text_dy"] + "em") .attr("transform", "rotate(-" + options['x_rotate'] + ")") svg.append("g") .attr("class", "y axis") .call(yAxis) .append("text") .attr("transform", "rotate(-90)") .attr("y", 9) .attr("dy", ".71em") .style("text-anchor", "end") .text(options['y_name']); var bar = svg.selectAll(".label") .data(dataset) .enter().append("g") .attr("class", "g") .attr("transform", function(d) { return "translate(" + x(d[group_field]) + ",0)"; }); var bar_enter = bar.selectAll("rect").data(function(d) { return d.values; }).enter(); bar_enter.append("rect") .attr('class', 'stacked_bar') .attr("width", x.rangeBand()) .attr("y", function(d) { return y(d.y1); }) .attr("height", function(d) { return y(d.y0) - y(d.y1); }) .style("fill", function(d) { return color(d.name); }); bar_enter.append("text") .text(function(d) { return d3.format(".2s")(d.y1-d.y0) + "%"; }) .attr("y", function(d) { return y(d.y1)+(y(d.y0) - y(d.y1))/2; }) .attr("x", x.rangeBand()/2) .style("fill", options["bar_font_color"]) .style("text-anchor", 'middle') .style("font-size", options['bar_font_size']); left_bar = dataset.slice(0, dataset.length-1) right_bar = dataset.slice(1) for(var idx in left_bar){ l = left_bar[idx] r = right_bar[idx] x1 = x(l[group_field]) + x.rangeBand() x2 = x(r[group_field]) l_vs = l.values for(var vidx in l_vs){ y1 = y(l_vs[vidx]['y1']) y2 = y(r.values[vidx]['y1']) svg.append("line") .attr("x1", x1) .attr("y1", y1) .attr("x2", x2) .attr("y2", y2) .attr("stroke-width", options["stroke_width"]) .attr("stroke", options["stroke_color"]) .style("stroke-dasharray", "5, 5") }}; if(options['vertical_index']){ vertical_index = options['vertical_index'] - 1 a = x(left_bar[vertical_index][group_field]) b = x(right_bar[vertical_index][group_field]) + x.rangeBand() svg.append("line") .attr("x1", (a + b) / 2) .attr("y1", -10) .attr("x2", (a + b) / 2) .attr("y2", height) .attr("stroke-width", 1.0) .attr("stroke", "black") .style("stroke-dasharray", "3, 3") } max_class_name_len = d3.max(color.domain(), function(d) { return d.length }) console.log(color.domain()) svg.append("g") .attr("class", "legendLinear") .attr("d", color.domain().reverse()) // .attr("d", color.domain() .attr("transform", "translate(-" + max_class_name_len * 17 + ",0)"); //.attr("transform", "translate("0,"+(height+40)+")"); // svg.selectAll("cell").call(_stacked.wrap, 50); /* var legend = d3.legend .color() .shapeWidth(height/15) .shapePadding(8) //.orient('horizontal') .scale(color) svg.select(".legendLinear") .call(legend) */ // legend label 크기 //cellLabels = $(".legendCells .label"); //_stacked.wrap(cellLabels, 100) //cellLabels.each(function(d){ //cellLabels[d].textContent = _stacked.wrap(cellLabels[d].textContent,100) // cellLabels[d].textContent = _stacked.wrap(cellLabels[d].textContent,100) //}) } }; UnitRendererFactory.add("stacked_barchart", _stacked.render);