Qt绘制热力图

0.前言

热力图是一种重要的数据可视化方式,可以直观地展现空间数据的疏密程度或频率高低。本文将使用QPainter+QImage来实现热力图绘制,效果参照百度ECharts图表库:

《从前慢》 --------木心

记得早先少年时

大家诚诚恳恳

说一句 是一句

清早上火车站

长街黑暗无行人

卖豆浆的小店冒着热气

从前的日色变得慢

车,马,邮件都慢

一生只够爱一个人

从前的锁也好看

钥匙精美有样子

你锁了 人家就懂了

目录

1.实现思路

2.实现细节

3.代码链接

4.参考

1.实现思路

主要步骤如下:

a.每个单独的点为径向渐变色的圆,且透明度从中心到边缘逐渐降低

    QRadialGradient gradient(posX,posY,radius); //径向渐变
    gradient.setColorAt(0,QColor(0,0,0,alpha)); //点的透明度
    gradient.setColorAt(1,QColor(0,0,0,0));
    painter.setBrush(gradient);
    painter.drawEllipse(QPointF(posX,posY),radius,radius);

b.多个点组合时,透明度是互相叠加的,所以我们先用透明度绘制一个Image(透明度就表示热度)

    //透明度用Format_Alpha8就行了
    _dataImg=QImage(ImgWidth,ImgHeight,QImage::Format_Alpha8);
    _dataImg.fill(Qt::transparent);
    //接下来追加绘制圆点

c.如果一个点出现多次,那么就需要有权重的概念,可以根据重复最多的点来作为计算的标准,也可以根据重复次数分段

    //权重统计表(把权重单独拿出来,就可以不用遍历数据点来计算了)
    //加()初始化为0
    int *_countTable=new int[ImgWidth*ImgHeight]();
    //以最大次数来计算该点的权重
    const uchar alpha=uchar(_countTable[posX+posY*ImgWidth]/(double)_maxCount*255);

d.透明度的图绘制完成后就可以填充颜色,颜色根据透明度从计算好的颜色表中查表获得

void MainWindow::drawHeatImg()
{
    //把alpha值转为颜色值
    for(int row=0;row<_dataImg.height();row++)
    {
        //dataimg QImage::Format_Alpha8,一个点1个字节
        const uchar *line_data=_dataImg.scanLine(row);
        //heatimg QImage::Format_ARGB32,一个点4个字节
        QRgb *line_heat=reinterpret_cast<QRgb*>(_heatImg.scanLine(row));
        for(int col=0;col<_dataImg.width();col++)
        {
            //根据alpha透明度从颜色表取颜色
            line_heat[col]=_colorList[line_data[col]];
        }
    }
}

2.实现细节

a.颜色表的实现

颜色表我使用的线性渐变会知道Image来取颜色的方式,因为单纯 QLinearGradient 好像没有取某个点颜色的接口;还有就是计算颜色透明度的时候可能要考虑热力图整体的透明度。

颜色表的元素类型为 QRgb ,是 uint32 的 typedef ,表示 #AARRGGBB。

    //根据线性渐变色条得到颜色表
    QLinearGradient linear=QLinearGradient(QPoint(0,0),QPoint(255,0));
    linear.setColorAt(0, Qt::blue);
    linear.setColorAt(0.4, Qt::blue);
    linear.setColorAt(0.5, Qt::cyan);
    linear.setColorAt(0.6, Qt::green);
    linear.setColorAt(0.8, Qt::yellow);
    linear.setColorAt(0.95, Qt::red); //255对应透明度最大值,即中心点

    //把渐变色绘制到Img方便取颜色
    QImage img(256,1,QImage::Format_ARGB32);
    QPainter painter(&img);
    painter.fillRect(img.rect(),linear);

    //HeatAlpha为热力图整体透明度
    quint32 alpha=0;
    for(quint32 i=0;i<256;i++){
        //根据热力图透明度来计算颜色表的透明度
        alpha=HeatAlpha/255.0*i;
        //typedef unsigned int QRgb: format #AARRGGBB
        //颜色+透明度
        _colorList[i]=img.pixel(i,0)&0x00FFFFFF|(alpha<<24);
    }

b.点的追加

如果点不是重合的,那么直接在透明度图中追加绘制就行,如果点重合了,那么出于权重对透明度的影响,需要重新绘制透明度图。

    //为什么不直接if(pos_count>_maxCount)才重新绘制?
    //而是有两个点叠加if(pos_count>1)就重新绘制?
    //因为单纯的叠加目前还没有带入权重来计算,如果最大权重更大,那么这个叠加的点颜色就走样了
    if(pos_count>1){
        if(pos_count>_maxCount)
            _maxCount=pos_count;
        drawDataImg();
    }else{
        drawDataPoint(pt);
    }

c.透明度图转热力图

因为我们是先绘制的透明度图来表示热度,再通过透明度查表来填充的颜色,所以我们需要两个Image。

    //data用alpha叠加
    _dataImg=QImage(ImgWidth,ImgHeight,QImage::Format_Alpha8);
    _dataImg.fill(Qt::transparent);
    //热力图通过alpha值查表
    _heatImg=QImage(ImgWidth,ImgHeight,QImage::Format_ARGB32);
    _heatImg.fill(Qt::transparent);

 绘制热力图的时候,我们使用 QImage 的 scanLine() 函数,以及颜色查表来提高效率。

    //把alpha值转为颜色值
    for(int row=0;row<_dataImg.height();row++)
    {
        //dataimg QImage::Format_Alpha8,一个点1个字节
        const uchar *line_data=_dataImg.scanLine(row);
        //heatimg QImage::Format_ARGB32,一个点4个字节
        QRgb *line_heat=reinterpret_cast<QRgb*>(_heatImg.scanLine(row));
        for(int col=0;col<_dataImg.width();col++)
        {
            //根据alpha透明度从颜色表取颜色
            line_heat[col]=_colorList[line_data[col]];
        }
    }

3.代码链接

代码git链接:

https://github.com/gongjianbo/MyTestCode/tree/master/Qt/MyHeatMap

实现效果:

4.参考

JS版:https://blog.csdn.net/HiddlestonCloud/article/details/83743449

Qt版:https://blog.csdn.net/pbe_sedm/article/details/8982357

发布了110 篇原创文章 · 获赞 36 · 访问量 13万+

猜你喜欢

转载自blog.csdn.net/gongjianbo1992/article/details/104566768
今日推荐