浅谈Heatmap
在自然界之中,蛇的眼睛有夜视功能,即便是茫茫黑夜,它也能轻而易举的找到猎物,这是因为任何物体都会辐射热红外,且辐射的高低和温度成正比,由于生命体的体温会明显高于周围环境的温度,所以在蛇眼面前便无处遁形。热红外成像被广泛应用于军事领域,士兵带上能识别热红外的眼镜后能轻而易举的发现藏匿的敌人。
热红外成像
唠叨了半天,听上去似乎有点跑题了,其实不然,对互联网从业者而言,同样需要有火眼金睛,以便识别网友的喜好,此时的衡量标准是点击,点击越多则表示越喜欢,此技术被称作Heatmap,已经有网站提供此类服务,如:clickdensity,clicktale,crazyegg等等,甚至还有类似clickheat项目提供源代码供你直接使用。
不过最灵活的方案莫过于自己搞定,下面大概说说Heatmap的实现:
捕捉点击
当然,这需要Javascript来实现。为了不陷入浏览器兼容的泥潭,我们选择JQuery:
<script>
jQuery(document).ready(function() {
$(document).mousedown(function(e) {
if (e.clientX >= $(window).width() || e.clientY >= $(window).height()) {
return;
}
$.get("/path/to/a/empty/html/file", {
page_x : e.pageX,
page_y : e.pageY,
screen_width : screen.width,
screen_height: screen.height
});
});
});
</script>
客户端使用Ajax通过GET方法触发一个空HTML页面,当然,还可以更简单点:
<script> var image = new Image();
image.src = "..."; </script>
之所以要记录屏幕分辨率是因为有的情况下需要修正点击坐标。比如说,一个居中显示的定宽的页面,其同一个位置在不同分辨率下的坐标是不同的,当渲染图片的时候,坐标需要以一个分辨率为准进行修正。
另外,如果用户正在拖动滚动条,是不应该记录的。
分析日志
客户端使用Ajax通过GET方法触发一个空HTML页面,如此就会在服务端留下日志:
page_x=...&page_y=...&screen_width=...&screen_height=...
不同的日志格式,结果会有所不同,这里仅仅以此为例来说明问题,本文采用AWK来解析日志,当然你也可以使用Perl或别的你熟悉的语言:
#!/usr/bin/awk -f
BEGIN {
FS="&";
}
NF == 4 {
param["page_x"] = "0";
param["page_y"] = "0";
param["screen_width"] = "0";
param["screen_height"] = "0";
split($0, query, "&");
for (key in query) {
split(query[key], item, "=");
if (item[1] in param) {
param[item[1]] = item[2];
}
}
print "page_x:" , param["page_x"];
print "page_y:" , param["page_y"];
print "screen_width:" , param["screen_width"];
print "screen_height:", param["screen_height"];
print "\n";
}
至于数据的持久化,是使用MongoDB或者别的,自己定夺,这里就不多说了。
渲染图片
出于演示方便的考虑,我使用了一些随机生成的数据,以Imagick为例,代码如下:
<?php
$coordinates = array();
for ($i = 0; $i < 1000; $i++) {
$coordinates[] = array(rand($i, 1000), rand($i, 1000));
}
$max_repeat = max(
array_count_values(
array_map(function($v) { return "{$v[0]}x{$v[1]}"; }, $coordinates)
)
);
$opacity = 1 - 1 / $max_repeat;
$heatmap_image = new Imagick();
$heatmap_image->newImage(1000, 1000, new ImagickPixel('white'));
$heatmap_image->setImageFormat('png');
$plot_image = new Imagick('plot.png');
$iterator = $plot_image->getPixelIterator();
foreach($iterator as $row) {
foreach ($row as $pixel) {
$colors = $pixel->getColor();
foreach (array('r', 'g', 'b') as $channel) {
$color = $colors[$channel];
if ($color !== 255) {
$colors[$channel] = $color + ((255 - $color) * $opacity);
}
}
$pixel->setColor("rgb({$colors['r']},{$colors['g']},{$colors['b']})");
}
$iterator->syncIterator();
}
$plot_size = $plot_image->getImageGeometry();
foreach ($coordinates as $pair) {
$heatmap_image->compositeImage(
$plot_image,
Imagick::COMPOSITE_MULTIPLY,
$pair[0] - $plot_size['width'] / 2,
$pair[1] - $plot_size['height'] / 2
);
}
$color_image = new Imagick('clut.png');
$heatmap_image->clutImage($color_image);
$heatmap_image->writeImage('heatmap.png');
?>
代码虽然很多,但并不复杂,其中用到了两个图片,分别是:plot.png和clut.png。实际应用时,有时候点击量会非常大,此时没有必要把所有的点击都渲染出来,而应该采取随机取样的策略,如果采用MongoDB持久化的话,可以参考:The Random Attribute。
备注:代码参考image-tempest。
最终展示
形象一点来说,其实就是通过CSS+Javascript把生成的图片盖在网页上,并调节图片透明度来达到合二为一的效果,篇幅所限,具体代码留给大家自己实现,例子效果可参考下图:
Heatmap
BTW:热点可能会随着时间改变,为了能对照某个时间的网页,可以使用CutyCapt截屏。顺手再贴一个相关的项目:smt2(simple mouse tracking)。
有关Heatmap的详细介绍,还可以参考
收工!Heatmap虽然不是很复杂的技术,但涉及的方面却很繁杂,希望本文能帮到大家。
浅谈Heatmap的更多相关文章
- 浅谈 Fragment 生命周期
版权声明:本文为博主原创文章,未经博主允许不得转载. 微博:厉圣杰 源码:AndroidDemo/Fragment 文中如有纰漏,欢迎大家留言指出. Fragment 是在 Android 3.0 中 ...
- 浅谈 LayoutInflater
浅谈 LayoutInflater 版权声明:本文为博主原创文章,未经博主允许不得转载. 微博:厉圣杰 源码:AndroidDemo/View 文中如有纰漏,欢迎大家留言指出. 在 Android 的 ...
- 浅谈Java的throw与throws
转载:http://blog.csdn.net/luoweifu/article/details/10721543 我进行了一些加工,不是本人原创但比原博主要更完善~ 浅谈Java异常 以前虽然知道一 ...
- 浅谈SQL注入风险 - 一个Login拿下Server
前两天,带着学生们学习了简单的ASP.NET MVC,通过ADO.NET方式连接数据库,实现增删改查. 可能有一部分学生提前预习过,在我写登录SQL的时候,他们鄙视我说:“老师你这SQL有注入,随便都 ...
- 浅谈WebService的版本兼容性设计
在现在大型的项目或者软件开发中,一般都会有很多种终端, PC端比如Winform.WebForm,移动端,比如各种Native客户端(iOS, Android, WP),Html5等,我们要满足以上所 ...
- 浅谈angular2+ionic2
浅谈angular2+ionic2 前言: 不要用angular的语法去写angular2,有人说二者就像Java和JavaScript的区别. 1. 项目所用:angular2+ionic2 ...
- iOS开发之浅谈MVVM的架构设计与团队协作
今天写这篇博客是想达到抛砖引玉的作用,想与大家交流一下思想,相互学习,博文中有不足之处还望大家批评指正.本篇博客的内容沿袭以往博客的风格,也是以干货为主,偶尔扯扯咸蛋(哈哈~不好好工作又开始发表博客啦 ...
- Linux特殊符号浅谈
Linux特殊字符浅谈 我们经常跟键盘上面那些特殊符号比如(?.!.~...)打交道,其实在Linux有其独特的含义,大致可以分为三类:Linux特殊符号.通配符.正则表达式. Linux特殊符号又可 ...
- 浅谈Angular的 $q, defer, promise
浅谈Angular的 $q, defer, promise 时间 2016-01-13 00:28:00 博客园-原创精华区 原文 http://www.cnblogs.com/big-snow/ ...
随机推荐
- emoji等表情符号存mysql的方法
项目中需要存储用户信息(用户昵称有表情符号),自然就遇到了emoji等表情符号如何被mysql DB支持的问题 这里引用先行者博文:https://segmentfault.com/a/1190000 ...
- docker:安装mysql
文章来源:https://www.cnblogs.com/hello-tl/p/9234429.html 1.添加镜像 docker pull mysql 2.在/data下新建文件夹mysql,进入 ...
- 第一章 pyhton基础
一 .pyhton2与python3的区别 在pyhton2中,其中编码默认使用的是ascii编码,输出格式为print"xxx",输入为raw_input(“请输入”),在整型中 ...
- [译]The Python Tutorial#9. Classes
写在前面 本篇文章是<The Python Tutorial>(3.6.1),第九章,类的译文. 9. Classes 与其他编程语言相比,Python的类机制定义类时,最小化了新的语法和 ...
- Django项目 生成依赖包
生成依赖包文件 requirements.txt,最好用 pip3, 如果有 python2 的话会默认使用 py2 的 pip pip3 -r freeze > requirements.tx ...
- (转)ios 代码规范
转自http://blog.csdn.net/pjk1129/article/details/45146955 引子 在看下面之前,大家自我检测一下自己写的代码是否规范,代码风格是否过于迥异阅读困难? ...
- 剑指Offer(书):反转链表
题目:输入一个链表,反转链表后,输出新链表的表头. 分析:要分清他的前一个节点和后一个节点,开始的时候前节点为null,后节点为head.next,之后,反转. public ListNode Rev ...
- 总线(bus);设备(devices);驱动(drivers)
Linux Cross Reference Free Electrons Embedded Linux Experts • Source Navigation • Diff Markup • Id ...
- strcpy与strcat函数原型
1.strcpy函数原型 char *my_strcpy(char *dest,const char *src) //const使在函数中不能修改*src其原先的值{ char *strDest ...
- PAT Basic 1062
1062 最简分数 一个分数一般写成两个整数相除的形式:N/M,其中 M 不为0.最简分数是指分子和分母没有公约数的分数表示形式. 现给定两个不相等的正分数 N1/M1 和 N2/M ...