java+highchart实现分类下钻柱形图

可视化展示中, 向下钻取的功能很常见. 这是有必要掌握的技能之一.

首先看官方示例.


java+highchart实现分类下钻柱形图_第1张图片
第一层

java+highchart实现分类下钻柱形图_第2张图片
点击第一个柱子向下钻取

实现这种图形展示, 关键点和难点在于数据格式的准备上. 通过查看示例代码, 可以看出, 主要需要两个参数: seriesdrilldown:series.
经过测试发现, series可以支持两种格式: 1. json格式和数组格式.

为了叙述方便, 这里定义,,区/县表示3级. 本文以实现这样的3级下钻为例.

关键图形参数

series

       series: [{
            name: '浏览器品牌',
            colorByPoint: true,
            data: [{
                name: 'Microsoft Internet Explorer',
                y: 56.33,
                drilldown: 'Microsoft Internet Explorer'
            }, {..},..]

其中, data中的元素笔者将其称作点(point), 包含name-鼠标悬停显示的分类名称,y-数值, drilldown-寻找下钻后的数据的锚, drilldown:series中会有对应的id. (注: 最低一级一般不需要drilldown参数, 当然有不会影响显示, 这就为封装数据提供了小便利).

drilldown:series

 drilldown: {
            series: [{
                name: 'Microsoft Internet Explorer',
                id: 'Microsoft Internet Explorer',
                data: [
                    [
                        'v11.0',
                        24.13
                    ],
                    [..],...
                }]
            }
  • 所有的需要向下钻取的数据都在drilldown字段里. 即市和区的数据都在drilldown里配置.
  • 这里的series格式和上文的series格式一样.
  • 这里的data格式为数组样式. 经测试, 也可以用上文的json样式. 这又为封装数据提供了便利. 注意:省级和市级由于需要向下钻取, 所以data不能为数组格式(没有那么智能, 差评).

需求

省市区对应了若干小分区. 现需要分组统计出每个省的分区个数占比, 即分布情况. 当下钻某个省时, 显示该省下所有市的分布情况...

数据库格式:

分区
安徽 合肥 滨湖 007分区

后台数据封装

由于数据需要后台利用java查询数据库, 封装数据, 以json格式响应给highchart. 所以, 封装数据上需要一番研究.

javabean设计

下文两种方式, 基于相同的Javabean设计.


java+highchart实现分类下钻柱形图_第3张图片
Javabean设计
  • Series对应了highchart中的series字段.
  • Point代表一个点的数据, 多个Point构成了data.

方式一:

思路:

  1. 先查询出所有省的分组统计结果.
  2. 准备用两个变量, series存储省相关数据, drilldownSeries存储drilldown相关数据, 即市和区/县的.
  3. 取数据封装数据, 构建series类, 添加至drilldownSeries. 同时, 根据当前遍历到的省份, 根据名称查询旗下的所有市统计数据.
  4. 然后遍历市级数据封装数据, 添加至drilldownSeries, 同时根据当前遍历到的市, 查询旗下的所有区/县统计数据. 封装成series类, 添加至drilldownSeries.

代码:

public Map getData4DrillDownChart() {
        // 一级: 省; 二级: 市; 三级: 区/县
        // 获取分区总数, 作为分母, 计算百分比.
        long s = dao.count();
        double sum = s;

        // 查询所有省及分组统计数据

        List objecList = dao.findAreaByGroup();
        // 定义省级series, 对应highchart的series属性
        Series series = new Series();
        series.setName("分区统计分布图");

        // 省级(第一级)统计数据处理
        for (Object[] province : objecList) {
            Series.Point point = new Series.Point();
            point.setName((String) province[0]);
            long y = (long) province[1];
            double perc = y/sum;
            // 设置占比
            point.setY(perc);
            // drilldown ID就用省份名称
            point.setDrilldown(point.getName());

            // 添加至data
            series.getData().add(point);
        }
        // 省级数据封装完成

        /*[[下钻数据*/
        // drilldown:series
        List drilldownSeries = new ArrayList<>();
        List level_1_data = series.getData();
        // 遍历所有省的名称
        for (Series.Point p : level_1_data) {
            // 查找对应的市级统计数据
            List city_DB = dao.findByProvinceNameAndCountDistrictByProvinceAndCity(p.getName());
            //定义drilldown里面的series, 市级series
            Series series1 = new Series();
            series1.setId(p.getDrilldown());
            series1.setName(p.getName());

            /*市级
            * 遍历市级数据设置点集合(即前端的data属性)
            * 并向下获取当前市的县级数据,构造series,添加至drilldownSeries*/
            for (Object[] city : city_DB) {
                Series.Point point = new Series.Point();
                point.setName((String) city[0]);
                Long y = (Long) city[1];
                double perc = y/sum;
                point.setY(perc);
                // 二级drilldown用二级的名称
                point.setDrilldown(point.getName());
                // 添加点
                series1.getData().add(point);



                /*县级
                * 根据当前市的名称获取对应县的数据, 并构造县级series, 添加至drilldownSeries*/
                String cityName = point.getName();
                // 获取当前市下的所有县级统计数据
                // TODO: 2018/1/11 考虑到不同省可能有重名的城市名==>根据省名和市名联合查询 or 优化表设计,设置好ID.
                List district_DB = dao.findByCityNameAndCountDistrictByProvinceCityAndDistrict(cityName);
                //定义drilldown里面的series, 县级series
                Series series2 = new Series();
                series2.setId(point.getDrilldown());
                series2.setName(point.getName());
                // 遍历县级统计数据,设置data
                for (Object[] district : district_DB) {
                    Series.Point point1 = new Series.Point();
                    point1.setName((String) district[0]);
                    Long y1 = (Long) district[1];
                    double perc1 = y1/sum;
                    point1.setY(perc1);
                    // 到县一级结束了, 不再drilldown
                    point1.setDrilldown(null);
                    // 添加至点集合
                    series2.getData().add(point1);
                    // TODO: 2018/1/11 如果还需要往下钻取, 则继续向下抓取相应数据\\程序需要优化(可以用递归)

                }
                // 添加至drilldownSeries
                drilldownSeries.add(series2);

            }

            // 市级的series属性设置完成, 现在将其添加至drilldownSeries
            drilldownSeries.add(series1);
        }
        /*]]下钻数据*/

        // 将series和drilldownSeries返回给action
        HashMap drilldownData = new HashMap<>();
        drilldownData.put("series", series);
        drilldownData.put("drilldownSeries", drilldownSeries);
        return drilldownData;

    }

方式2:

方式1虽然能够实现功能, 但是, 1.查询数据库的次数太多; 2.封装数据的业务逻辑复杂, 且不具有通用性, 硬编码.

方式2内容精彩, 所以另起一篇.
参见: java+highchart实现分类下钻柱形图[续]

附录

Series接口bean


import java.util.ArrayList;
import java.util.List;

/**
 * @author Nisus-Liu
 * @version 1.0.0
 * @email [email protected]
 * @date 2018-01-10-22:53
 */

//    name: '浏览器品牌',
//    id:'brands',
//    colorByPoint: true,
//    data: [{},{},..]
//    其中data:
//        name: 'Chrome',
//        y: 24.03,
//        drilldown: 'Chrome'
public class Series {
    private String name;
    private String id;
    private boolean colorByPoint = true;
    private List data = new ArrayList();

    public List getData() {
        return data;
    }

    public void setData(List data) {
        this.data = data;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    public boolean isColorByPoint() {
        return colorByPoint;
    }

    public void setColorByPoint(boolean colorByPoint) {
        this.colorByPoint = colorByPoint;
    }



    public static class Point{
        private String name;
        private double y;
        private String drilldown;

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }

        public double getY() {
            return y;
        }

        public void setY(double y) {
            this.y = y;
        }

        public String getDrilldown() {
            return drilldown;
        }

        public void setDrilldown(String drilldown) {
            this.drilldown = drilldown;
        }
    }
}

你可能感兴趣的:(java+highchart实现分类下钻柱形图)