zoukankan      html  css  js  c++  java
  • 使用 highchart 绘制柱状图的通用方法与接口


           本文给出使用 highchart 绘制柱状图的通用方法与接口, 只要指定相应的数据结构和配置, 就可以直接拿来使用。


           一、  数据结构与基本接口  

           一般绘制图形, 会涉及到较复杂的数据结构, 比如使用 jsPlumb 绘制拓扑图的通用接口 。方法是, 首先要弄清楚绘制图形所需要的数据结构,然后根据API文档设计一个公共接口, 并写好详细的文档,避免日后忘记。先从最基本的接口开始, 见下面代码。 这是根据静态示例, 将需要动态生成或配置数据的地方抽取出来做成的接口。      

         

    /**
     * 创建柱状图(基本接口)
     * @param chartDivName  用来绘制柱状图的 DIV-ID 值
     * @param chartData     柱状图数据结构
     *                      categories: ['c1', 'c2', ..., 'Cn']
     *                      series: [
     *                          { name: 'var1', data: [d11, d12, ..., d1n]},  
     *                          { name: 'var2', data: [d21, d22, ..., d2n]},
     *                          ...,
     *                          { name: 'varN', data: [dn1, dn2, ..., dnn]}
     *                      ]
     * @param chartConfig  柱状图全局配置
     *                        title:  图表标题
     * @returns
     */
    function generateColumnChart(chartDivName, chartData, chartConfig) {
        var displayFormatter = function() {  // 当鼠标悬停图表上时, 格式化提示信息
            var tipText = '<b>' + this.x + '</b>';
            var total = 0;
            $.each(this.points, function(i, point) {
                total += point.y;
            });
            $.each(this.points, function(i, point) {
                tipText += '<br/>'+ point.series.name +': '+ Highcharts.numberFormat((point.y*100 / total), 2) + '%' + '(' + point.y + ')';
            });
            return tipText;
        };
        
        var chartObj = obtainCommonChartObj(displayFormatter);
        chartObj.title.text = chartConfig.title;
        chartObj.xAxis.categories = chartData.categories;
        chartObj.series = chartData.series;
        var seriesNum = (chartData.series == null ? 0 : chartData.series.length);
        for (var k=0; k < seriesNum; k++) {
            chartObj.series[k].type = 'column';
        }
        
        var chartdiv = $('#'+chartDivName);
        chartdiv.highcharts(chartObj);
    }
    
    
    function obtainCommonChartObj(displayFormatterFunc) {
        
           var commonChartObj = {
                chart: {
                    zoomType: 'x',
                    events: {
                        click: null
                    },
                    resetZoomButton: {
                        position: {
                            x: -10,
                            y: 10
                        },
                        relativeTo: 'chart'
                    }
                },
                
                // 去掉 highcharts.com 链接
                credits: {
                    enabled: false,
                    text: ''
                },
                
                plotOptions: {
                    series: {
                        // 去掉点的marker, 使图形更美观
                        marker: {
                            enabled: false,
                            states: {
                                hover: {
                                    enabled: true
                                }
                            }
                        },
                        turboThreshold: 0,
                        events: {
                            click: null
                        }
                    },
                    line: {
                        lineWidth: 1.5
                    }
                },
                
                series: [],
                xAxis: {
                },
                yAxis: {
                    title: {
                        text: ''
                    },
                    min: 0
                },
                
                tooltip: {
                    crosshairs: true,
                    shared: true,
                    formatter: displayFormatterFunc
                },
                
                title: {
                    // 动态显示图表标题
                    text: '',
                    align: 'center',
                    style: {
                        fontSize: '12px',
                        margin: '3px'
                    }
                }
            };
           return commonChartObj ;
    }
        
    
    

           二、 对象数组与结构转化

           通常, 从服务端后台返回的数据结构是对象数组, 要使用基本接口来绘制, 就需要进行数据结构转化。 因此, 在基本接口之上, 可以构建一个高层接口, 见如下代码所示。 如果要转化的数据结构比较复杂, 那么, 可以拿一个静态的输入/输出数据示例作为范本来辅佐思考, 先通过硬编码来实现目标, 然后再将硬编码用可配置项替换掉, 达到可扩展、灵活的目标。   

      

    /**
     * 创建柱状图(针对对象数组的高层接口)
     * @param chartDivName  用来绘制柱状图的 DIV-ID 值
     * @param chartData     对象数组
     *                      categories: ['c1', 'c2', ..., 'Cn']
     *                      data:
     *                      [{'field1': 'v11', 'field2': 'v12', ..., 'fieldN': 'v1N'},
     *                       {'field1': 'v21', 'field2': 'v22', ..., 'fieldN': 'v2N'},
     *                       ..., 
     *                       {'field1': 'vN1', 'field2': 'vN2', ..., 'fieldN': 'vNN'}]
     * @param chartConfig   柱状图全局配置
     *                       title: 图表标题
     *                       categoryField: 分类字段
     *                       groupField: 用于创建 legend 的分组字段
     *                       valueField: 用于显示 Y 轴的字段
     * @returns
     */
    function generateColumnChartHighLevel(chartDivName, chartData, chartConfig) {
        
        var groupField = chartConfig.groupField;
        var valueField = chartConfig.valueField;
        var categoryField = chartConfig.categoryField;
        var categories = chartData.categories;
        
        var groupedChartData = groupByField(chartData.data, groupField);
        
        var series = [];
        
        for (var i=0; i< groupedChartData.length; i++) {
            var groupName = groupedChartData[i][groupField];
            var groupData = groupedChartData[i]['data'];
            
            var fieldData = [];
            for (var k=0; k < groupData.length; k++) {
                // 每个分类的值必须与相应的分类对应, 应对这样的情况
                // 对于每个 groupField, 并不是所有 categories 都有值, 可以通过测试例子看出来
                // 苹果在 Q3 对应的值是缺失的, 香蕉在 Q2 对应的值是缺失的 
                var categoryPosition =  getCategoryPosition(categories, groupData[k][categoryField]);
                if (categoryPosition != -1) {
                    fieldData[categoryPosition] = groupData[k][valueField];
                }
            }
            for (var j=0; j < categories.length; j++) {
                // 缺失值填充
                if (fieldData[j] == null) {
                    fieldData[j] = 0;
                }
            }
            var subseries = {
                name: groupName,
                data: fieldData,
            };
            series.push(subseries);
        }
        
        var data = {};
        data.categories = categories;
        data.series = series;
        generateColumnChart(chartDivName, data, chartConfig);
        
    }
    
    
    /**
     * 检测 value 在 categories 中的位置
     * @param categories
     * @param value
     */
    function getCategoryPosition(categories, value) {
        for (var index=0; index < categories.length; index++) {
            if (categories[index] == value) {
                return index;
            }
        }
        return -1;
    }
    
    
    /**
     * 将指定 chartData 数据按照指定字段的值分类
     * eg. [{'timestamp': 'time0', cpu: '0', sys: '15', usr: '20'}, {'timestamp': 'time0', cpu: '1', sys: '16', usr: '21'},
     *      {'timestamp': 'time1', cpu: '0', sys: '20', usr: '13'}, {'timestamp': 'time1', cpu: '1', sys: '18', usr: '10'}]
     * 转换为 [{ cpu:'0', data: [{'timestamp': 'time0', cpu: '0', sys: '15', usr: '20'}, {'timestamp': 'time1', cpu: '0', sys: '20', usr: '13'}] } ,
     *       { cpu:'1', data: [{'timestamp': 'time0', cpu: '1', sys: '16', usr: '21'}, {'timestamp': 'time1', cpu: '1', sys: '18', usr: '10'}] }] 
     */
    var groupByField = function(chartDataGathered, fieldName) {
        var fieldDataMappingArray = [];
        var fieldData = {};
        var i=0, num = (chartDataGathered == null ? 0 : chartDataGathered.length);
        for (i=0; i<num; i++) {
            var fieldValue = chartDataGathered[i][fieldName];
            fieldData = obtainFieldData(fieldDataMappingArray, fieldName, fieldValue);
            if (fieldData == null) {
                fieldData = {};
                fieldData[fieldName] = fieldValue;
                fieldData['data'] = [];
                fieldDataMappingArray.push(fieldData);
            }
            fieldData['data'].push(chartDataGathered[i]);
        }
        return fieldDataMappingArray;
    }
    
    
    /**
     * 在 fieldDataMappingArray 中检测是否有 fieldName = fieldValue 的对象, 若有则返回; 若没有则返回 null
     * @param fieldDataMappingArray [{'fieldName': 'fieldValue1', 'data':[]}, {'fieldName': 'fieldValue2', data: []}]
     * @param fieldName the name of field
     * @param fieldValue the value of field
     */
    var obtainFieldData = function(fieldDataMappingArray, fieldName, fieldValue) {
        var k=0, dataArrayLength = (fieldDataMappingArray == null ? 0 : fieldDataMappingArray.length);
        var fieldData = {};
        var existFieldData = {};
        if (dataArrayLength == 0) {
            return null;
        }
        
        for (k=0; k<dataArrayLength; k++) {
            existFieldData = fieldDataMappingArray[k];
            if (existFieldData[fieldName] == fieldValue) {
                return existFieldData;
            }
        }
        return null;
    }

         

           三、 使用接口     

      

    <!doctype html public "-//w3c//dtd html 4.01//en" "http://www.w3.org/tr/html4/strict.dtd">
    <html>
        <head>
            <meta http-equiv="content-type" content="text/html; charset=utf-8">
            <title>highcharts 绘图示例</title>
            <script src="jquery-1.10.1.min.js"></script>
            <script src="highcharts.js"></script>
            <script src="draw_highcharts.js"></script>
    
            <script type="text/javascript">
                
                $(document).ready(function() {
    
                     /**
                       * 一个用于测试基本接口的示例
                       * @param chartDivId
                       */
                    function testGenerateColumnChart(chartDivId) {
                        var categories = ['2013-11', '2013-12', '2014-01'];
                        var series = [
                             { name: '苹果', data: [1500, 1300, 1200] },
                             { name: '桔子', data: [3500, 5000, 2500] },
                             { name: '香蕉', data: [2000, 1800, 1600] }
                        ];
                        var data = {
                            categories: categories, series: series    
                        };
                        var chartConfig = { title: '第四季度水果销量' };
                        generateColumnChart(chartDivId, data, chartConfig);
                     }
                     testGenerateColumnChart('testBasicColumnchartdiv');        
    
                    /**
                     * 测试绘制柱状图高层接口的测试例子
                     * @param chartDivId
                     */
                    function testGenerateColChartHighLevel(chartDivId) {
                        var data = [
                            { time: 'Q1' , fruit: '苹果', sale: 1500 },             
                            { time: 'Q1' , fruit: '桔子', sale: 1300 },
                            { time: 'Q1' , fruit: '香蕉', sale: 1400 },
                            { time: 'Q2' , fruit: '苹果', sale: 1500 }, 
                            { time: 'Q2' , fruit: '桔子', sale: 1900 },
                            { time: 'Q3' , fruit: '桔子', sale: 1700 },
                            { time: 'Q3' , fruit: '香蕉', sale: 1800 }
                        ];
                        
                        var categories = ['Q1', 'Q2', 'Q3'];
                        
                        var chartData = {
                            categories: categories,
                            data: data
                        };
                        
                        var chartConfig = {
                            title: '季度水果销量',
                            categoryField: 'time',
                            groupField: 'fruit',
                            valueField: 'sale'
                        }
                        generateColumnChartHighLevel(chartDivId, chartData, chartConfig);
                    }
                    testGenerateColChartHighLevel('testAdvancedColumnchartdiv');
                    
                });
            
            
            </script>
    
            <style>
                body {
                    font-family: '微软雅黑', '宋体', 'san-serif';
                }
    
                .chartdiv {
                    width: 90%;
                    height: 250px;
                }
            </style>
    
        </head>
    
        <body>
    
            <div id="testBasicColumnchartdiv" class="chartdiv"></div>
            <div id="testAdvancedColumnchartdiv" class="chartdiv"></div>
        </body>
    </html>
    
    

            四、 效果图

             

            五、 小结

            要绘制柱状图, 需要对数据结构和算法有较好的掌握, 能够自由地在各种数据结构中进行转换。通过此例, 是想再次说明了结构与算法在实际开发工作中的应用。 当然, 文中给出的代码并非是最优的, 作为一个基本的解法, 里面还是有很多可改进之处。 

     

  • 相关阅读:
    jvm内存配置参数
    6 个设计原则分别是什么?每种设计原则体现的设计模式是哪个?
    classloader 结构,是否可以自己定义一个 java.lang.String 类,为什么? 双亲代理机制。
    求sum=1+111+1111+........+1....111 .
    交换排序
    字符串压缩 stringZip
    TCP为何采用三次握手来建立连接,若采用二次握手可以吗
    使用jsoup抓取新闻信息
    通过地址获得经纬度(百度Geocoding API)
    FileReader
  • 原文地址:https://www.cnblogs.com/lovesqcc/p/4037709.html
Copyright © 2011-2022 走看看