浅谈Heatmap

在自然界之中,蛇的眼睛有夜视功能,即便是茫茫黑夜,它也能轻而易举的找到猎物,这是因为任何物体都会辐射热红外,且辐射的高低和温度成正比,由于生命体的体温会明显高于周围环境的温度,所以在蛇眼面前便无处遁形。热红外成像被广泛应用于军事领域,士兵带上能识别热红外的眼镜后能轻而易举的发现藏匿的敌人。

热红外成像

热红外成像

唠叨了半天,听上去似乎有点跑题了,其实不然,对互联网从业者而言,同样需要有火眼金睛,以便识别网友的喜好,此时的衡量标准是点击,点击越多则表示越喜欢,此技术被称作Heatmap,已经有网站提供此类服务,如:clickdensityclicktalecrazyegg等等,甚至还有类似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.pngclut.png。实际应用时,有时候点击量会非常大,此时没有必要把所有的点击都渲染出来,而应该采取随机取样的策略,如果采用MongoDB持久化的话,可以参考:The Random Attribute

备注:代码参考image-tempest

最终展示

形象一点来说,其实就是通过CSS+Javascript把生成的图片盖在网页上,并调节图片透明度来达到合二为一的效果,篇幅所限,具体代码留给大家自己实现,例子效果可参考下图:

Heatmap

Heatmap

BTW:热点可能会随着时间改变,为了能对照某个时间的网页,可以使用CutyCapt截屏。顺手再贴一个相关的项目:smt2(simple mouse tracking)。

有关Heatmap的详细介绍,还可以参考

收工!Heatmap虽然不是很复杂的技术,但涉及的方面却很繁杂,希望本文能帮到大家。

浅谈Heatmap》上有22个想法

  1. Pingback引用通告: heatmap.js 一个用canvas画热力图的利器 | A.I

  2. Pingback引用通告: 国内外 Heatmap 、热图的应用和相关技术 | 橡子树博客

  3. Pingback引用通告: HeatMap(热图)的原理和实现 - 收容站

  4. Pingback引用通告: Heatmap,热图的应用和相关技术 - DataVlab.org

  5. 你好!我现在想通过很多点数据(包含经纬度),制作热力地图反映哪些区域的点分布比较密集。 了解了百度热力图API示例(http://developer.baidu.com/map/jsdemo.htm#c1_15),源代码中的point带有count属性,这里count是表达什么含义?另外,我的实验点数据仅仅包含经纬度信息,不知道能否通过百度API实现热力地图?求大神!!

    • 这种问题你咨询百度的技术支持不是更合理么?

  6. 利用HeatMap.js实现热图,数据量特别大,max设置多少合适呢?我试过max比较小时(20),这样显示的热图就比较平均,看不出太明显的差异,max比较大(200)时,很多点都显示不出来

  7. Pingback引用通告: 谈谈网页中的Heatmap热点图 - 问说网

  8. Pingback引用通告: 热力地图调研与使用 – Xiang的收藏

发表评论

电子邮件地址不会被公开。 必填项已用*标注