Spring Framrwork

C3 Bar Chart

gregorio 2018. 2. 5. 16:56

C3는 D3 Chart 기반으로 쉽게 Chart를 생성할 수 있는 Chart 솔루션이다.


C3 Chart는 다양한 Chart를 제공하고 있으며, 개발 시 http://c3js.org/를 참조하여 개발을 수행할 수 있다.


C3 Chart를 사용하면서 데이터를 생성하는데 이슈가 되었던 부분들을 간략하게 설명한다.


■ JSON 데이터 처리

C3 Chart는 Json Data를 사용할 수 있다고 되어 있으나, 예제를 찾을 수 없어 column으로 데이터를 처리하였다.

또한 java script에서 loop를 최소화하기 위해 Oracle의 PIVOT 기능을 이용하여 데이터 조회 시 Row를 Column으로 변환하였다.



■ PIVOT을 이용한 Row를 Column으로 변환


SELECT *

FROM (

     SELECT TO_CHAR(START_TIME, 'YYYY-MM-DD') START_DAY

          ,INTERFACE_ID

     FROM TRANS_LOG

     WHERE 1 = 1

<if test='startDate != "" and startDate != null'>

     <![CDATA[AND START_TIME >=  #{startDate}]]>

    </if>

<if test='interfaceId != "" and interfaceId != null'>

AND   INTERFACE_ID LIKE '%' || #{interfaceId} || '%'

</if>

)

PIVOT 

(

COUNT(START_DAY)

    FOR START_DAY IN

    <foreach collection="condDate" item="item"  open="(" close=")" separator=",">

            ${item}

        </foreach>

)  


PIVOT을 사용할 경우 바인딩 변수를 사용할 수 없으며, 바인딩 변수를 사용하면 ORA-56900 오류가 발생한다.


이를 해결하기 위해서 Mybatis의 #{itme} 대신에 ${item}을 사용하여 해결하였다.


또한 PIVOT의 IN 내에 조건을 바인딩하기 위해 사전에 다음 SQL로 데이터를 조회한 후 조회결과를 파라미터를 전송하였다.


■ PIVOT의 IN 바인딩 데이터 조회

SELECT q'[']' || START_DATE || q'[']' START_DATE

FROM   

   (SELECT DISTINCT(TO_CHAR(START_TIME, 'YYYY-MM-DD')) START_DATE FROM TRANS_LOG

WHERE 1 = 1

<if test='startDate != "" and startDate != null'>

<![CDATA[AND   START_TIME >= #{startDate}]]>

</if>

<if test='interfaceId != "" and interfaceId != null'>

AND   INTERFACE_ID LIKE '%' || #{interfaceId} || '%'

</if>

ORDER BY 1


START_DATE를 값으로 사용하기 위해 조회 시 Single Quotation을 붙여주었다.


 


■ JSP Source

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">

<html>

<head>

<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">


<link rel="stylesheet" href="/resources/css/jquery-ui-1.10.1.css">

<link rel="stylesheet" href="/resources/css/table.css">

<link rel="stylesheet" href="/resources/css/c3.min.css">

<link rel="stylesheet" href="/resources/css/melon.datepicker.css">




<script type="text/javascript" src="/resources/js/jquery-3.3.1.min.js"></script>

<script type="text/javascript" src="/resources/js/jquery-ui-1.10.1.min.js"></script>  

<script type="text/javascript" src="/resources/js/c3.min.js"></script>

<script type="text/javascript" src="/resources/js/d3.v3.min.js" charset="utf-8"></script>


<script>


$(document).ready(function() {

$("#btnSearch").click(function(e) {

    e.preventDefault();

    

debugger;

var objectData =

         {

             startDate: $('#startDate').val(),

             chartType: $('#chartType').val(),

             interfaceId: $('#interfaceId').val()

         };


var sendData = JSON.stringify(objectData);

    $.ajax({

        type: "POST",

        url: '/chart/showStatistics.do',

        data : sendData,

        dataType : "json",  //Cross domain

        contentType: "application/json",

        success : function(response, status, xhr) {

        alert(response);

        debugger;

        console.log(response); 

        showChart(response);

        }, 

        error: function(jqXHR, textStatus, errorThrown) {

        debugger;

        console.log(jqXHR.responseText); 

        }

    });

});


});


//Date Picker

$(function() {

    $( "#startDate" ).datepicker({

    defaultDate: new Date(),

    dateFormat: "yy-mm-dd",

    changeMonth: true, 

        changeYear: true,

        dayNames:['Sun', 'Mon', 'Tue', 'Wed', 'Thr', 'Fri', 'Sat']

    });

});

function showChart(response) {

debugger;

var graphType =  $('#chartType').val().toUpperCase() + " Statistic Graph";

$( "#graphType" ).text(graphType);

var categories=[];

$.each(response.categories, function(idx, data) {

categories.push(data);

});

var columns = [];

$.each(response.columns, function (idx, data) {

var col = [];

var idx = 0;

$.each(data, function(key, value) {

debugger;

col[idx] = value;

idx++;

});

columns.push(col);

});

var chart = c3.generate({

bindto: "#interfacechart",

    size: {

        height: 600,

        width: 1200

    },

    data: {

        type: 'bar',

        columns: columns,

    },

    bar: {

        width: {

            ratio: 0.3 // this makes bar width 50% of length between ticks

        }

        // or

        //width: 100 // this makes bar width 100px

    },

    axis: {

    x: {

    type: 'category',

    categories:categories

    }

    },

    grid: {

        x: {

            show: true

        },

        y: {

            show: true

        }

    }

});


// setTimeout(function () {

// chart.load({

//         columns: [

//             ['data3', 130, -150, 200, 300, -200, 100]

//         ]

//     });

// }, 1000);

}


</script>

<title>Interface Monitoring</title>

</head>


<body>

<!-- Search Area -->

<div class="container">

        <form action="">

        <div class="fieldName">

        <label>START DATE</label>

        </div>

        <div class="select">

            <input type="text" id="startDate" size=10 style="align=center;"/>

        </div>

       

        <div class="fieldName">

            <label>Statistic Period</label>

  </div>

        <div class="select">

            <select name="chartType" id="chartType">

        <option value="daily">Daily</option>

        <option value="monthly">Monthly</option>

    </select>

</div>

        <div class="fieldName">

            <label>Interface Name</label>

  </div>

<div class="data">

<input type="text" id="interfaceId" title="Enter Interface Name" size="30"></input>

</div>

        <div class="logbutton">

            <button  id="btnSearch">Search</button>

        </div>

  </form>

</div>


<div>

<label style="height:100%;padding:5px; margin-top:10px; font-family:Sans-serif; font-size:1.2em;" id="graphType">Statistic Graph</label>

</div>

<div id="interfacechart" style="width: 1200px; height: 500px;"></div>

</body>

  

</body>

</html> 



■Controller 소스


import java.util.HashMap;

import java.util.List;

import java.util.Map;


import javax.annotation.Resource;


import org.springframework.stereotype.Controller;

import org.springframework.web.bind.annotation.RequestBody;

import org.springframework.web.bind.annotation.RequestMapping;

import org.springframework.web.bind.annotation.ResponseBody;

import org.springframework.web.servlet.ModelAndView;


import cmn.util.base.BaseController;

import cmn.util.dao.CamelMap;

import sehati.inf.chart.service.ChartService;


@Controller

public class CharController extends BaseController {


@Resource(name = "chartService")

private ChartService chartService;

@RequestMapping("/chart/viewChart")

public ModelAndView viewChart() throws Exception {

ModelAndView mav = new ModelAndView();

mav.setViewName("InterfaceStatistics");

return mav;

}

@RequestMapping(value="/chart/showStatistics.do")

public @ResponseBody Map<String, Object> showStatistics(@RequestBody Map<String, Object> param) throws Exception {


Map<String, Object> resultMap = new HashMap<String, Object>();

/** Search Condition **/

List<String> categories = chartService.selectPivotQueryTarget(param);

param.put("condDate", categories);

List<CamelMap> resultList = chartService.selectChartData(param);


resultMap.put("categories", categories);


resultMap.put("columns", resultList);

return resultMap;

}

}

 


먼저 날짜별 PIVOT을 적용하기 위해 날짜를 조회한 후 실제 데이터 조회하기 위해 condData를 파라미터로 전달한다.