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

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

首先看官方示例.


第一层

点击第一个柱子向下钻取

实现这种图形展示, 关键点和难点在于数据格式的准备上. 通过查看示例代码, 可以看出, 主要需要两个参数: 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设计.


Javabean设计
  • Series对应了highchart中的series字段.
  • Point代表一个点的数据, 多个Point构成了data.

方式一:

思路:

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

代码:

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

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

        List<Object[]> 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<Series> drilldownSeries = new ArrayList<>();
        List<Series.Point> level_1_data = series.getData();
        // 遍历所有省的名称
        for (Series.Point p : level_1_data) {
            // 查找对应的市级统计数据
            List<Object[]> 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<Object[]> 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<String, Object> 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 liuhejun108@163.com
 * @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<Point> data = new ArrayList<Point>();

    public List<Point> getData() {
        return data;
    }

    public void setData(List<Point> 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;
        }
    }
}

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,711评论 5 468
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,932评论 2 376
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,770评论 0 330
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,799评论 1 271
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,697评论 5 359
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,069评论 1 276
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,535评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,200评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,353评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,290评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,331评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,020评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,610评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,694评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,927评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,330评论 2 346
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,904评论 2 341