首页 新闻 论坛 群组 Blog 文档 下载 读书 Tag 网摘 搜索 开源 FAQ 第二书店 博文视点 程序员
频道: 研发 数据库 中间件 信息化 视频 .NET Java 游戏 移动 服务: 人才 外包 培训
    图书品种:235680
       
热门搜索: ASP.NET Ajax Spring Hibernate Java

5.5  透视图决策分析方案

透视图决策分析方案是统计图决策分析方案的综合应用,这里是通过JSP技术和JFreeChart技术联合实现的,JFreeChart技术负责绘制统计图,JSP技术负责实现透视图的效果。

1.方案分析

利用透视图开发决策分析模块,能够为用户提供灵活的操作空间,例如该案例,可以由用户控制统计的商品及时间,用户在操作透视图时,它的可控流程如所图5.46显示。

图5.46  透视图统计模块的可控流程

从图5.46可以看出,在开发透视图时,图例字段和分类字段是受页字段影响的,每次修改页字段,图例字段和分类字段都会随之变化,例如本案例,每修改页字段月份,图例字段都会发生变化,只提供当月有销售记录的商品。

本案例的透视图效果是通过一个完全独立的JSP页实现的,负责绘制统计图的JFreeChart代码也放在了该页中,目的是增强代码的可移植性,在需要透视图统计方式的地方调用该JSP就可以了,本案例的透视图效果及关键实施方案如图5.47所示。

图5.47  本案例的透视图效果及关键实施方案

2.实施过程

*  实例位置光盘\mr\5\5.5\01

在开发图形统计模块时,还可以模仿Excel提供的透视图功能,利用JSP页开发透视图统计功能,透视图为人机对话提供了良好的操作页面,用户通过透视图,可以更好的分析数据,如图5.48所示,用户可以首先可以选择要查看的月份,然后还可以选择要查看的商品或时段。

图5.48  透视图决策分析方案效果图

在图5.47中已经给出了透视图的效果及关键实施方案,下面将详细介绍透视图的实施过程,以及在编码过程中需要注意的事项,并且按照图5.46所示的可控流程进行讲解。

下面的代码负责实现页字段,页字段是单选的,并且提供了一个“全部”选项,当选中该选项时,代表选中所有的页字段选项,该选项为默认选项。这里为代表页字段的菜单控件添加了onchange事件,每当用户修改了该菜单控件的值,都会重新请求该页,并传入用户选中的值,具体代码如下:

例程5-73  代码位置:光盘\mr\5\5.5\01\StatChart\scenograph.jsp

<td class="otherTitle"><%=phasesTitle %>:

  <select name="phasesSelected" onchange="requestCurrentJsp(this.value)">

    <option value="All">全部</option>

    <%

   if(phasesSelected.equals("All")){

        for(int i=0;i<phases.length;i++){

        out.print("<option value='"+phases[i]+"'>"+phases[i]+"</option>");

        }

   }else{

        for(int i=0;i<phases.length;i++){

        if(phases[i].equals(phasesSelected))

            out.print("<option selected alue='"+phases[i]+"'>"+phases[i]+"</option>");

        else

            out.print("<option value='"+phases[i]+"'>"+phases[i]+"</option>");

        }

   }

    %>

  </select>&nbsp;</td>

下面的代码负责实现图例字段,图例字段是多选的,也提供了一个“全部”选项,当选中该选项时,代表选中所有的图例字段选项,该选项为默认选项。用户订制完图例字段后,单击下方的“确定”按钮,也会重新请求该页,并传入用户选中的值,具体代码如下:

例程5-74  代码位置:光盘\mr\5\5.5\01\StatChart\scenograph.jsp

<tr align="center">

  <td class="otherTitle"><%=cutlineTitle %></td>

</tr>

<tr align="center">

  <td><select name="cutlineSelected" size="<%=multipleSize %>" multiple>

    <option value="All">全部</option>

    <%

    if(cutlineSelected[0].equals("All")){

        for(int i=0;i<cutline.length;i++){

           out.print("<option value='"+cutline[i]+"'>"+cutline[i]+"</option>");

        }

    }else{

        for(int m=0;m<cutline.length;m++){

           boolean isSelected=false;

            for(int n=0;n<cutlineSelected.length;n++){

                   if(cutline[m].equals(cutlineSelected[n])){

                      isSelected=true;

                      break;

                   }

            }

           if(isSelected){

             out.print("<option selected value='"+cutline[m]+"'>"

                  +cutline[m]+"</option>");

           }else{

             out.print("<option value='"+cutline[m]+"'>"+cutline[m]+"</option>");

           }

        }

    }

    %>

  </select></td>

</tr>

<tr align="center">

  <td><input type="submit" name="Submit2" value="确定"></td>

</tr>

下面的代码负责实现分类字段,分类字段也是多选的,同样提供了一个“全部”选项,当选中该选项时,代表选中所有的分类字段选项,该选项为默认选项。用户订制完分类字段后,单击下方的“确定”按钮,也会重新请求该页,并传入用户选中的值,具体代码如下:

例程5-75  代码位置:光盘\mr\5\5.5\01\StatChart\scenograph.jsp

<tr align="center">

  <td class="otherTitle"><%=categoryTitle %></td>

</tr>

<tr align="center">

  <td><select name="categorySelected" size="<%=multipleSize %>" multiple>

    <option value="All">全部</option>

    <%

    if(categorySelected[0].equals("All")){

        for(int i=0;i<category.length;i++){

           out.print("<option value='"+category[i]+"'>"+category[i]+"</option>");

        }

    }else{

        for(int m=0;m<category.length;m++){

           boolean isSelected=false;

            for(int n=0;n<categorySelected.length;n++){

                   if(category[m].equals(categorySelected[n])){

                      isSelected=true;

                      break;

                   }

            }

           if(isSelected){

              out.print("<option selected value='"+category[m]+"'>"

                   +category[m]+"</option>");

           }else{

              out.print("<option value='"+category[m]+"'>"+category[m]+"</option>");

           }

        }                

    }

    %>

  </select></td>

</tr>

<tr align="center">

  <td><input type="submit" name="Submit" value="确定"></td>

</tr>

*  注意:代表图例字段的列表控件和代表分类字段的列表控件位于同一个form表单中,当需要对两个字段的值进行修改时,可以同时修改,然后单击任何一个“确定”按钮都可以!

下面的代码负责获得由用户选中的页字段、图例字段和分类字段的值,默认情况下均为选中“全部”选项。

例程5-76  代码位置:光盘\mr\5\5.5\01\StatChart\scenograph.jsp

String phasesSelected="All";

String[] cutlineSelected={"All"};

String[] categorySelected={"All"};

if(request.getParameter("phasesSelected")!=null){

phasesSelected=request.getParameter("phasesSelected");

    if(request.getParameterValues("cutlineSelected")!=null){

      cutlineSelected=request.getParameterValues("cutlineSelected");

    }

    if(request.getParameterValues("categorySelected")!=null){

      categorySelected=request.getParameterValues("categorySelected");

    }

}

在每次请求实现透视图的scenograph.jsp页时,都会执行下面的代码,从数据库获得页字段的选项,具体代码如下:

例程5-77  代码位置:光盘\mr\5\5.5\01\StatChart\scenograph.jsp

phases=dataStat.selectOneColumn("select (cast(datepart(yy,date) as char(4))+'-'

    +cast(datepart(mm,date) as char(2))) as 'yy-mm' from tb_merchSale group by

    (cast(datepart(yy,date) as char(4))+'-'+cast(datepart(mm,date) as char(2))),

    datepart(yy,date),datepart(mm,date) order by datepart(yy,date) desc,

    datepart(mm,date) desc");

下面的代码负责获得图例字段的选项,在组织用来检索图例字段选项的SQL语句时,需要依据页字段的值,代码如下:

例程5-78  代码位置:光盘\mr\5\5.5\01\StatChart\scenograph.jsp

String selectCutlineSql="select name+'('+model+')' as cutline from tb_merchInfo

    where id in(select merchInfo_id from tb_merchSale";

if(!phasesSelected.equals("All"))

      selectCutlineSql+=" where datepart(yy,date)="+phasesSelected.substring(0,4)+"

          and datepart(mm,date)="+phasesSelected.substring(5);

selectCutlineSql+=" group by merchInfo_id) order by cutline desc,id desc";

cutline=dataStat.selectOneColumn(selectCutlineSql);

本案例的分类字段的选项不受页字段值的影响,是固定的,代码如下:

category=new String[]{"上旬","中旬","下旬"};

下面开始封装绘图数据,在封装绘图数据时分为两种情况,一种情况是分类字段的第一个值为“全部”,另一种情况是分类字段的值为非“全部”的任何值。任何一种情况,都要根据图例的选择情况,创建double型的实例,用来保存统计出的绘图数据。

首先看分类字段的第一个值为“全部”的情况。由于在这种情况下需要统计所有分类字段的数据,所以是按照图例进行封装数据的,具体代码如下:

例程5-79  代码位置:光盘\mr\5\5.5\01\StatChart\scenograph.jsp

if(cutlineSelected[0].equals("All"))

  data=new double[cutline.length][category.length];

else

  data=new double[cutlineSelected.length][category.length];

upData=dataStat.selectMoreColumn(upSql); // 统计上旬的数据

middleData=dataStat.selectMoreColumn(middleSql); // 统计中旬的数据

downData=dataStat.selectMoreColumn(downSql); // 统计下旬的数据

for(int m=0;m<cutlineId.length;m++){ // 按照图例进行封装

  for(int n=0;n<upData.length;n++){

     if((upData[n][0]+"").equals(cutlineId[m])){

         data[m][0]=upData[n][1];

         break;

     }

  }

  for(int n=0;n<middleData.length;n++){

     if((middleData[n][0]+"").equals(cutlineId[m])){

         data[m][1]=middleData[n][1];

         break;

     }

  }

  for(int n=0;n<downData.length;n++){

     if((downData[n][0]+"").equals(cutlineId[m])){

         data[m][2]=downData[n][1];

         break;

     }

  }

}

然后看分类字段的第一个值为非“全部”的任何值情况。由于在这种情况下并不需要统计出所有分类的数据,所以需要按照选择的分类值进行封装,具体代码如下:

例程5-80  代码位置:光盘\mr\5\5.5\01\StatChart\scenograph.jsp

if(cutlineSelected[0].equals("All"))

  data=new double[cutline.length][categorySelected.length];

else

  data=new double[cutlineSelected.length][categorySelected.length];

for(int i=0;i<categorySelected.length;i++){ // 按照选择分类值的个数进行循环

  if(categorySelected[i].equals("上旬")){

     upData=dataStat.selectMoreColumn(upSql);

     for(int m=0;m<cutlineId.length;m++){

         for(int n=0;n<upData.length;n++){

                if((upData[n][0]+"").equals(cutlineId[m])){

                   data[m][i]=upData[n][1];

                   break;

                }

         }

     }

  }

  if(categorySelected[i].equals("中旬")){

     middleData=dataStat.selectMoreColumn(middleSql);

     for(int m=0;m<cutlineId.length;m++){

        for(int n=0;n<middleData.length;n++){

                if((middleData[n][0]+"").equals(cutlineId[m])){

                   data[m][i]=middleData[n][1];

                   break;

                }

         }

     }

  }

  if(categorySelected[i].equals("下旬")){

     downData=dataStat.selectMoreColumn(downSql);

     for(int m=0;m<cutlineId.length;m++){

         for(int n=0;n<downData.length;n++){

                if((downData[n][0]+"").equals(cutlineId[m])){

                   data[m][i]=downData[n][1];

                   break;

                }

         }

     }

  }

}

下面将通过上面处理得到的信息,定义图例、分类和JFreeChart的绘图数据集,具体代码如下:

例程5-81  代码位置:光盘\mr\5\5.5\01\StatChart\scenograph.jsp

DefaultCategoryDataset dataset = new DefaultCategoryDataset(); // 定义绘图数据集

String[] cutlineOver=cutlineSelected; // 定义图例

String[] categoryOver=categorySelected; // 定义分类

if(cutlineSelected[0].equals("All"))

    cutlineOver=cutline;

if(categorySelected[0].equals("All"))

    categoryOver=category;

for (int m = 0; m < cutlineOver.length; m++) {

    for (int n = 0; n < categoryOver.length; n++) {

        dataset.addValue(data[m][n], cutlineOver[m], categoryOver[n]);

    }

}

现在就可以绘制统计图了,在绘制统计图时,将根据图例的多少分为两种情况,当图例较多时,如果绘制图例,将占用图片很大的空间,所以在这种情况下不绘制图例,而是对统计图提供热点标签,如图5.48所示;当图例较少时,则绘制图例,但是不提供热点标签,而是通过普通标签标注统计数据,如图5.49所示。

图5.49  图例较少时的透视图效果

为了实现上述效果,需要定义两个boolean型的属性,分别用来控制是否绘制图例和是否提供热点标签,默认为图例较少,接着利用这两个属性创建一个JFreeChart实例,具体代码如下:

例程5-82  代码位置:光盘\mr\5\5.5\01\StatChart\scenograph.jsp

boolean legend=true; // 默认绘制图例

boolean tooltips=false; // 默认不提供热点标签

if(cutlineOver.length>10){ // 当图例的个数大于10时,则相反

    legend=false;

    tooltips=true;

}

JFreeChart chart = ChartFactory.createBarChart3D(

        "",

        xTitle,

        yTitle,

        dataset,

        PlotOrientation.VERTICAL,

        legend,

        tooltips,

        false);

当legend属性的值为true时,将执行下面的代码,绘制用来标注统计数据的普通标签。

例程5-83  代码位置:光盘\mr\5\5.5\01\StatChart\scenograph.jsp

if(legend){

    renderer.setItemLabelGenerator(new StandardCategoryItemLabelGenerator(

         "{2}", new DecimalFormat("¥#,###.00"))); // 格式化普通标签

    renderer.setItemLabelsVisible(true); // 设置普通标签为可见,JFreeChart默认为不可见

    ItemLabelPosition p = new ItemLabelPosition( // 设置标签的显示位置

         ItemLabelAnchor.CENTER, TextAnchor.CENTER_LEFT,

         TextAnchor.CENTER_LEFT, -Math.PI / 2.0

         );

    renderer.setPositiveItemLabelPositionFallback(p);

}

当tooltips属性的值为true时,将执行下面的代码,对热点标签进行格式化。

例程5-84  代码位置:光盘\mr\5\5.5\01\StatChart\scenograph.jsp

if(tooltips)

      renderer.setBaseToolTipGenerator(new StandardCategoryToolTipGenerator(

                  " [ {0},{1} ] = {2} ", new DecimalFormat("¥#,###.00")));

当tooltips属性的值为true时,还将执行下面的代码,作用是向JSP页输出MAP信息,供热点标签使用。

例程5-85  代码位置:光盘\mr\5\5.5\01\StatChart\scenograph.jsp

if(tooltips)

      ChartUtilities.writeImageMap(new PrintWriter(out), "chartInfo", info, false);

至此,一个完整的透视图案例就实施完成了!

3.补充说明

在运用透视图方案开发统计模块时,当调用本案例中的scenograph.jsp时,如果在订制透视图时出现如图5.50所示效果,是因为在重新请求JSP页时编码错误导致的,解决的办法是在Web程序的欢迎页加入如下代码:

request.setCharacterEncoding("GBK");

上面代码的功能是用来设置request请求的编码方式,这里设置为GBK,例如本案例是在index.jsp页设置的。

*  注意:在Web程序的非欢迎页加入上述代码是不能解决问题的!

图5.50  图例较少时的透视图效果

查看所有评论(0)条】

最近评论



正在载入评论列表...
热点评论