`
远去的渡口
  • 浏览: 475736 次
  • 性别: Icon_minigender_2
  • 来自: 上海转北京
社区版块
存档分类
最新评论

JfreeChart热点map的应用

阅读更多

    今天主要总结一下JfreeChart中热点map的应用,根据我个人的理解来总结的,可能会有某些地方不是很正确,如有错误,请大家指正,不断改进。

     首先,这里的热点的应用在于用户与JfreeChart图像的交互,也就是与图像文件(因为我们是将Chart对象以二进制数据写入一个图像文件了,比如PNG文件,JPEG图像文件)交互。在HTML中,为了让一个图像具有可交互的功能就必须给该图像定义一个Map对象,然后在

<img id="chartimage" src="${linechart }" usemap="#${filename }" >

也就是在img标签的usemap属性上,指定map对象。

下面举例说明,用JfreeChart在这个图像中产生热点。

 

那么它的原始map数据大致为:

<map id="jfreechart-23846.png" name="jfreechart-23846.png">

<area shape="poly" coords="675,93,681,93,681,99,675,99,675,93,675,93" title="日平均攝取熱量: (09-12-28 上午12:00, 1,788)" alt=""/>

<area shape="poly" coords="276,96,282,96,282,102,276,102,276,96,276,96" title="日平均攝取熱量: (09-11-27 上午12:00, 1,741.8)" alt=""/>

<area shape="poly"

<area shape="poly" coords="225,97,231,97,231,103,225,103,225,97,225,97" title="日平均攝取熱量: (09-11-23 上午12:00, 1,722.4)" alt=""/>

<area shape="poly" coords="115,24,114,26,112,27,110,26,109,24,110,22,112,21,114,22,115,24,115,24" title="日平均消耗熱量: (09-11-14 上午12:00, 3,027.8)" alt=""/>

</map>

这里面,shape是指热点的区域,本段中的poly是一个多边形,也有Rectangle等。Coords是这个多边形的坐标,通常是需要用工具生成,这里所展示的是JfreeChart自动生成的。Title=””中的内容便是mouse移到各个点时,展示出的信息,mouse移开后信息不再显示。这里不需要我们再写别的代码,个人理解是,浏览器可以解析这部分数据,相当于将title的内容在onmouseover中显示。

   那么,现在的问题就在于,怎么获得map数据了。

    

当然是根据一个图像来生成对应的MAP对象。

在创建一个图表对象时候有两个参数,比如这里面的折线图的部分代码:

JFreeChart jfreechart = ChartFactory.createTimeSeriesChart(title, "",

          y, xydataset, true, true, false);

两个参数就是这个方法中的最后两个参数,这两个参数的类型都是布尔值。这两个参数意思分别是:是否创建工具提示(tooltip)以及是否生成URL。这两个参数分别对应着MAP中一个AREAtitle属性以及href属性。

可是我想知道的是怎么来产生这个MAP啊!哈哈,不要着急,JFreeChart已经帮我们做好生成MAP对象的功能。为了生成MAP对象就要引入另外一个对象:ChartRenderingInfo。因为JFreeChart没有直接的方法利用一个图表对象直接生成MAP数据,它需要一个中间对象来过渡,这个对象就是ChartRenderingInfo。下图是生成MAP数据的流程图:

 

如上图所示,ChartUtilities类是整个流程的核心,它周围的对象都是一些例如数据对象或者是文件等。这个流程简单描述如下:首先创建一个ChartRenderingInfo对象并在调用ChartUtilitieswriteChartAsJPEG时作为最后一个参数传递进去。调用该方法结束后将产生一个图像文件以及一个填充好MAP数据的ChartRenderingInfo对象,有了这个对象我们还是没有办法获取具体的MAP数据,我们还必须借助于ChartUtilitieswriteImageMap方法来将ChartRenderingInfo对象读取出来,获取MAP数据的代码片断如下:

 

JFreeChart chart = createCaloriesLineChart(timesers, weekormonth,
				title, avgenergy + "(" + y + ")", index, week, year1,year,
				nodatamess, maxcalorie, month);

		HttpServletRequest request = io.getRequest();
		String filename = "";
		String graphURL = "";

		PrintWriter writer = null;
		try {
			io.getResponse().setContentType("text/html");
			io.getResponse().setCharacterEncoding("UTF-8"); // 这里要设置一下,因为这里相当于用PrintWriter将图片写出来,如果不设置字符集,则默认为ISO-8859-1,而页面用的是UTF-8.
			writer = io.getResponse().getWriter();
			Shape shape = new Rectangle(10, 5);
			ChartEntity entity = new ChartEntity(shape);
			StandardEntityCollection coll = new StandardEntityCollection();
			coll.add(entity);
			ChartRenderingInfo info = new ChartRenderingInfo(coll);
			filename = ServletUtilities.saveChartAsPNG(chart, 720, 250, info,
					io.getSession());
			ChartUtilities.writeImageMap(writer, filename, info, true);

			graphURL = request.getContextPath() + "/displayChart?filename="
					+ filename;

			
			String strimg = ChartUtilities.getImageMap(filename, info);

 

 

当初在实现这个拿到map数据时,花费了不少时间,根据writeImageMap()回溯到找寻如何得到 info,要得到info又如何得到StandardEntityCollection对象,以及StandardEntityCollectionChartEntityShape的关联,这个就需要花时间好好研究API了,感觉JfreeChart API写的太过于简单,需要自己动手试试一些方法才可以完成,不过感觉这部分还是比较单一的需求,感觉在图象对象其他的类上,过于复杂,文档过于简单,我们必须去了解每个类型的图表对象应该对应哪些AxisPlotRenderer类,并且必须非常熟悉这些类的构造函数中每个参数的具体含义才可以少走弯路,对于某些特定的需求,比如柱状图与折线图并存时,可不可以用XYPlot,只有试过,根据报错的信息才知道这样做是不可以的。好了,别的感慨不多说了。这里的strimg的内容就是map对象。

 

自己实现响应mouse事件

上图的map热点信息,大家可以看出,过于简单、表达的方式的不常规很难满足产品的需求,至少要以比较直观的、清晰的方式来表达信息,并且,如果单单只写出XY坐标对应的值,相信map热点的存在就是多余,just a Demo.所以为了满足产品级别需求,我们需要自己实现响应mouse事件。

   这个解决方案有多种,我是根据自己的理解,将很规则的map数据解析,根据X轴时间取到关联这一天、周、月的详细信息,然后将解析、重组后的数据以变量传到onmouseover事件中。

当然,怎么样让数据与map中各<area>标签的内容一致,以及后续的Tip定位问题,也是需要考虑的。为了方便,这里需要给每个area节点再加一个属性id,并赋值。

StringReader reader = new StringReader(strimg);
			InputSource source = new InputSource(reader);
			SAXBuilder build = new SAXBuilder();

			try {
				Document doc = build.build(source);
				Element element = doc.getRootElement();

				List<Element> list = element.getChildren();

				int size1 = list.size();
				for (int j = 0; j < size1; j++) {

					String nstr = "";

					Element e = list.get(j);
					Attribute att = e.getAttribute("title");
					String value = att.getValue();
					value = value.replace("(", "");
					value = value.replace(")", "");

					Attribute bute = new Attribute("id", j + "");
					e.setAttribute(bute);

					int id = bute.getIntValue();

					int ind = j;
					if (j >= size1 / 2) {
						ind = j - (size1 / 2);
					}

					UserFoodRecordBean r = (UserFoodRecordBean) calories
							.get(ind);

					double bike = r.getBikeCalorie();
					double bmrto = r.getBmrTotal();
					double sport = r.getSpcal();
					double foodcal = r.getCaloriesimple();
					double fect=r.getFoodfec();
					double cyclomer=r.getCycal();
					double total1 = r.getSportTotalCalorie();
					double balance = foodcal - total1;
					double dailywork = r.getDailyworkcalorie();
					String datestr = r.getUserfoodDate();

					if (weekormonth == 0) {
						datestr = BarChartData.formAtDate(datestr);
					} else {
						String[] das = datestr.split("-");
						int weekindex = Integer.parseInt(das[1]);

						if (weekormonth == 1) {
							
							datestr = das[0] + year + index1 + weekindex + week;
						} else {
							datestr = das[0] + year1  + weekindex + month;

						}
					}

					balance = Double.parseDouble(BarChartData
							.formAtCalorie(balance));

					nstr += "date=" + datestr + "&BMR=" + bmrto + "&";
					if (bike > 0) {
						nstr += "e-Bike=" + bike + "&";
					}
					if (sport > 0) {
						nstr += "sport=" + sport + "&";
					}
					if(cyclomer>0){
						nstr+="cyclemer="+cyclomer+"&";
					}
					if(fect>0){
						nstr+="fect="+fect+"&";
					}

					nstr += "food=" + foodcal + "&" + "total=" + total1
							+ "&balance=" + balance + "&dailywork=" + dailywork;

					// nstr=HtmlUtils.htmlUnescape(nstr);

					att.setValue("");// set nstr to title

					String over = "showChartTips('" + nstr + "','" + id
							+ "',event);";
					String out = "chartlivess();";

					Attribute bu = new Attribute("onmouseover", over);

					e.setAttribute(bu);

					Attribute outatt = new Attribute("onmouseout", out);
					e.setAttribute(outatt);

				}

				ByteArrayOutputStream baos = new ByteArrayOutputStream();
				PrintWriter pw = new PrintWriter(baos); // 用PrintWriter可以解决生成编码为UTF-8格式的XML内容出现中文乱码的问题


				Format format = Format.getPrettyFormat();
				format.setEncoding("UTF-8");

				XMLOutputter output = new XMLOutputter();
				output.setFormat(format);

				output.output(doc, pw);

				String strs = baos.toString();
				int j = strs.indexOf("<map");
				strs = strs.substring(j);
				io.setData("intakemap", strimg, BeanShare.BEAN_SHARE_REQUEST);

				

			} catch (JDOMException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}

 

 

 

 

然后以什么样式展示就与自己的审美有关了……当然可以是美工提供模版,呵

具体的过程就不用累赘了。

看看这样实现后的效果:

 

另外,在实现的过程中,还有一个问题也是花了许多时间 tip的定位问题,要兼容IE6IE7IE8FireFox,并且,出现滚动条时tip可能会错位问题,部分代码如下:

 

 var area= document.getElementById(id);
   if(area){
      var scroleft=document.documentElement.scrollLeft;
      var left=e.clientX+scroleft;
      var topy=e.clientY;
      var top= document.documentElement.scrollTop + topy;
  
  left-=66;
   
    var _tips=createTip1(); 
      _tips.style.width ="27em";
     // _tips.style.height="190px";
      _tips.style.display= ""; 
      
      _tips.style.left = left+"px";    
      _tips.style.top = top+"px"; 
  _tips.innerHTML=document.getElementById("tipmess").innerHTML;
  
   if(navigator.userAgent.indexOf('Firefox') >= 0){
    top=window.scrollY + e.clientY;
    left=window.scrollX+e.clientX;
    _tips.style.left=left+"px";
      _tips.style.top = top+"px"; 
      }
   
  
  }

 

 

PS: 部分内容转自http://www.ibm.com/developerworks/cn/java/l-jfreechart/ 作者 刘冬

 如果大家之前没有接触过JfreeChart或者是初学者,可以先看看这篇文章 。当初我在实现这个热点map时,感觉这篇文章写的很详细,对我也很有帮助,不过,我实现的具体方案还是经过多次尝试、多次调整,毕竟我这里的要求更高,呵呵

分享到:
评论
1 楼 happydrive 2010-08-21  
楼主,我看了之后还是有点迷茫,能上点完整的代码。

相关推荐

    struts2+MySQL+jfreechart生成带热点

    在"struts2+MySQL+jfreechart生成带热点"的场景中,我们可以构建一个Web应用,该应用能够从MySQL数据库中检索数据,然后使用JFreeChart生成包含热点的图表。热点是指图表上的区域,当用户点击这些区域时,可以触发...

    Check-Wikipedia-Backlinks-

    在实际应用中,这样的分析可以帮助我们了解Wikipedia的热点话题,发现哪些页面在互联网中被广泛引用,甚至可以用于学术研究,评估不同主题在公众视野中的关注度。同时,此项目也为Java开发者提供了一个学习和实践...

    COMSOL与MATLAB联合仿真调试及GUI界面设计:提升科研仿真效率的技术指南

    内容概要:本文详细介绍了COMSOL与MATLAB联合仿真的具体实现方法及其应用。首先讲解了如何通过LiveLink进行基本操作,如加载模型、设置参数并运行仿真。接着探讨了利用MATLAB的强大功能扩展COMSOL的功能,包括参数扫描、优化算法、批量处理等高级操作。随后,文章深入讨论了基于MATLAB App Designer设计友好的GUI界面的方法,使用户能够更加便捷地控制和展示仿真结果。此外,文中还分享了许多实用的调试技巧,如实时监控参数、异常处理以及内存管理等,确保仿真过程顺利进行。最后,强调了联合仿真在提高科研工作效率方面的巨大潜力。 适合人群:对数值模拟和科学计算感兴趣的科研工作者、工程师和技术爱好者,尤其是那些希望将COMSOL与MATLAB结合起来以增强仿真能力的人群。 使用场景及目标:适用于需要高效执行复杂仿真任务的研究项目,旨在帮助用户掌握COMSOL与MATLAB联合使用的最佳实践,从而显著减少仿真时间并获得更精确的结果。 其他说明:文章不仅提供了详细的代码示例,还分享了很多实践经验,有助于读者快速上手并解决实际问题。同时提醒读者关注不同版本间的兼容性和性能优化问题。

    等速万向节周期寿命试验台垂直运动模块及PLC控制设计 毕业设计说明书.doc

    等速万向节周期寿命试验台垂直运动模块及PLC控制设计 毕业设计说明书.doc

    钢筋混凝土肋梁楼盖设计课程设计说明书.doc

    钢筋混凝土肋梁楼盖设计课程设计说明书.doc

    是时候跟Mac系统自带的播放器说拜拜了!这个项目提供了简洁、高效的媒体播放体验,支持多种格式和平台,是你娱乐生活的绝佳伴侣

    特征 基于mpv,在macOS上提供最佳解码能力 设计时考虑了macOS(10.15+)的现代版本 视频和音乐所需的所有功能:字幕、播放列表、章节……以及更多! Force Touch、画中画和高级Touch Bar支持 可定制的用户界面,包括多种配色方案和屏幕控制器(OSC)布局定位 专为音频文件设计的独立音乐模式 视频缩略图 在线字幕搜索与智能本地字幕匹配 无限播放历史记录 方便的交互式视频/音频过滤器设置 完全可定制的键盘、鼠标、触控板和手势控制 面向高级用户的mpv配置文件和脚本系统 提供命令行工具和浏览器扩展

    基于Bagging集成模型的时间序列预测Matlab实现及其优化技巧

    内容概要:本文详细介绍了如何利用Bagging集成学习方法进行时间序列预测,并提供了完整的Matlab代码实现。首先解释了Bagging的基本原理,强调了在时间序列预测中保持序列连续性和防止信息泄露的重要性。然后展示了如何通过滑动窗口法生成样本矩阵,并使用ARIMA与XGBoost的混合模型作为基模型,实现了Bagging集成。文中还讨论了预测阶段的聚合策略,如指数衰减加权,以及并行加速技巧。此外,作者分享了一些实践经验,如避免使用复杂的深度学习模型作为基模型,以及如何处理异常值和特征工程的具体方法。 适合人群:具有一定编程基础和技术背景的研究人员、数据科学家和工程师,特别是对时间序列预测感兴趣的读者。 使用场景及目标:适用于需要提高时间序列预测精度的实际应用场景,如电力负荷预测、销售预测等。目标是通过集成学习方法减少预测误差,提高模型稳定性。 其他说明:文中提供的代码可以直接运行,同时附带了许多实用技巧和注意事项,帮助读者更好地理解和应用Bagging集成模型。

    苯 正丁醇化工原理课程设计说明书.pdf

    苯 正丁醇化工原理课程设计说明书.pdf

    4.0吨商用车膜片弹簧离合器设计说明书.doc

    4.0吨商用车膜片弹簧离合器设计说明书.doc

    常村煤矿22采区2203综采放顶煤工作面开采设计说明书.docx

    常村煤矿22采区2203综采放顶煤工作面开采设计说明书.docx

    基于多目标黏菌优化算法(MOSMA)的SVM参数优化及其在回归预测中的应用

    内容概要:本文介绍了一种创新的方法,利用多目标黏菌优化算法(MOSMA)来优化支持向量机(SVM)的参数C和gamma,从而提高回归预测的效果。首先详细解释了MOSMA的工作原理,包括黏菌权重更新、快速非支配排序以及自适应参数调整等关键技术点。接着展示了具体的Python代码实现,涵盖数据预处理、适应度函数定义、参数更新规则等方面。实验结果显示,在风电功率预测等多个应用场景中,相较于传统的网格搜索方法,MOSMA能够更快更有效地找到最优参数组合,显著提升了预测性能。 适合人群:从事机器学习研究或应用开发的技术人员,尤其是关注SVM参数优化及回归预测领域的从业者。 使用场景及目标:适用于需要进行高效参数寻优的回归预测任务,如风电功率预测、设备负载预测等。主要目标是通过改进SVM参数配置,获得更高的预测精度和更好的泛化能力。 其他说明:文中提供了完整的代码示例和详细的实施步骤指导,帮助读者快速理解和应用这一先进的优化技术。此外,还讨论了一些常见的注意事项和技术细节,如数据标准化、参数范围设定、并行化改造等。

    项目管理知识体系.ppt

    项目管理知识体系.ppt

    盾构机姿态与隧道中线自动化计算的Python实现及优化

    内容概要:本文详细介绍了如何利用Python进行盾构机姿态计算和隧道中线设计计算的自动化。首先,针对盾构机刀盘中心坐标的计算,提出了基于前后参考点距离的方法,并加入了异常值检测机制。其次,对于隧道中线的设计,采用三次样条曲线拟合,解决了控制点间距过大导致的“甩尾”现象。此外,文章还讨论了高程计算中的竖曲线处理方法,以及如何通过Pandas和Matplotlib实现自动报表生成和偏差分析。最后,强调了将工程经验转化为算法的重要性,旨在提高工作效率并减少人为错误。 适用人群:从事地铁隧道建设的技术人员、测量员、程序员及相关领域的研究人员。 使用场景及目标:适用于需要频繁进行盾构机姿态和隧道中线计算的工程项目,目标是提升计算效率和准确性,减轻工作人员负担,确保施工进度不受影响。 其他说明:文中提供了多个具体的Python代码片段,涵盖了从数据读取、处理到可视化的完整流程,为读者提供了一个完整的解决方案。同时,作者分享了许多实际应用中的经验和教训,帮助读者更好地理解和应用这些算法。

    计算机二级Word精选二十套(标红).jpg

    计算机二级Word精选二十套(标红)

    项目管理体系样本.doc

    项目管理体系样本.doc

    蓝桥杯单片机开发板测试视频.zip

    蓝桥杯

    一个本地DNS服务器,获取最快的网站IP,获得最佳上网体验,支持DoH,DoT

    SmartDNS 是一个运行在本地的 DNS 服务器,它接受来自本地客户端的 DNS 查询请求,然后从多个上游 DNS 服务器获取 DNS 查询结果,并将访问速度最快的结果返回给客户端,以此提高网络访问速度。 SmartDNS 同时支持指定特定域名 IP 地址,并高性匹配,可达到过滤广告的效果; 支持DOT,DOH,DOQ,DOH3,更好的保护隐私。 与 DNSmasq 的 all-servers 不同,SmartDNS 返回的是访问速度最快的解析结果。 支持树莓派、OpenWrt、华硕路由器原生固件和 Windows 系统等。

    基于S7-200 PLC和组态王的喷泉控制系统设计与实现

    内容概要:本文详细介绍了基于S7-200 PLC和组态王软件构建喷泉控制系统的全过程。首先明确了系统的IO分配,包括启动按钮、停止按钮以及喷泉水泵的连接方式。接着展示了梯形图程序的设计,涵盖了基本的启停控制逻辑、定时循环和模式切换机制。随后提供了详细的接线图原理图,解释了输入输出部分的具体接线方法。最后讲述了组态王的画面设计,包括创建工程、定义变量和绘制监控界面等步骤。此外还分享了一些调试过程中遇到的问题及解决方案。 适合人群:对自动化控制感兴趣的初学者和技术人员,尤其是那些希望深入了解PLC编程和人机界面设计的人群。 使用场景及目标:适用于小型喷泉项目的实际控制系统开发,旨在帮助读者掌握PLC编程技巧、熟悉组态软件的应用,并能够独立完成类似的自动化控制系统设计。 其他说明:文中不仅包含了理论知识讲解,还附带了许多实践经验分享,如硬件配置建议、常见错误规避措施等,有助于提高实际操作能力。

    DSP28335在线升级关键技术与实战经验分享:涵盖API使用、扇区划分、上位机开发及异常处理

    内容概要:本文详细介绍了DSP28335在线升级的关键技术和实践经验。首先,文章讲解了最小系统的构建以及MemCopy函数的作用,强调了Flash操作API的使用方法,包括初始化、扇区解锁与锁定、擦除和写入等步骤。接着,讨论了参数存储管理和CMD文件的编写要点,提出了双备份升级方案和增量升级的方法。同时,阐述了上位机通信协议的设计,包括帧结构、校验方式和数据传输的具体实现。此外,还探讨了常见的升级策略,如跳转式升级、镜像式升级和差分升级,并提供了详细的代码示例。最后,强调了异常处理的重要性,特别是针对断电情况的解决方案,如硬件看门狗和状态标记的使用。 适合人群:具有一定嵌入式开发基础的技术人员,尤其是熟悉DSP架构和C/C++编程的工程师。 使用场景及目标:适用于需要进行DSP28335在线升级的项目,帮助开发者避免常见错误,提高升级的成功率和可靠性。 其他说明:文中提供了大量实际操作中的注意事项和技巧,有助于读者更好地理解和应用相关技术。

    CDMA2000网络优化的研究毕业设计说明书.doc

    CDMA2000网络优化的研究毕业设计说明书.doc

Global site tag (gtag.js) - Google Analytics