项目需要在QML根据地图上的点位置绘制热力图,最终效果:
经过查阅相关的博客和实现方法,总结思路如下:
- 由于需要在QML地图中使用,因此使用QQuickPaintedItem绘制,并注册至QML中使用
- 使用QImage绘制渐变透明度图,以每个位置点中心绘制透明度渐变的圆,圆的半径就是热力图的衍射程度。这样同一个位置叠加的点越多,该位置的越不透明。假定所有点中重复度最高的位置(重复度为N)透明度为不透明,则每个衍射渐变圆的透明度就是255/N。
- 得到的QImage图片每个像素点透明度则代表了该位置点的重复程度,将该图片上每个像素点透明度转为不同的颜色,绘制成的新图片就是热力图。
头文件变量:
QList<QPoint> mPosList;//点位置列表
int mMaxCount;//最大重复数
int mAlpha;//每个位置点透明度
int mDiffraction = 15;
bool mLegendVisible;//热力图标识是否绘制
bool mHideZeroPoint;//无热力位置是否绘制
QImage alphaImage;//透明度图
QImage mColorImage;//热力图颜色标识
QImage mHeatImage;//最终热力图
QList<QRgb> colorList;//热力图颜色透明度对应表
绘制热力图标志和计算透明度/颜色映射表:
void MapHeatImage::drawColorImage()
{
QLinearGradient colorGradient = QLinearGradient(QPoint(0,255),QPoint(0,0));
colorGradient.setColorAt(0,Qt::blue);
colorGradient.setColorAt(0.2,Qt::cyan);
colorGradient.setColorAt(0.4,Qt::green);
colorGradient.setColorAt(0.6,Qt::yellow);
colorGradient.setColorAt(0.8,Qt::magenta);
colorGradient.setColorAt(0.95,Qt::red);
mColorImage = QImage(20,256,QImage::Format_ARGB32);
QPainter painter(&mColorImage);
painter.fillRect(mColorImage.rect(),colorGradient);
for(int i=255;i>-1;i--){
colorList.append(mColorImage.pixel(0,i));
}
}
绘制各点重叠后透明度图:
void MapHeatImage::paintHeat()
{
if(mPosList.isEmpty()){
return;
}
QPen pen;
pen.setWidth(0);
pen.setColor("#00000000");
alphaImage = QImage(this->width(),this->height(),QImage::Format_Alpha8);//只存有透明度
alphaImage.fill(Qt::transparent);
QPainter dataPainter;
dataPainter.begin(&alphaImage);
dataPainter.setPen(pen);
mAlpha = 255/mMaxCount;
for(QPoint point : mPosList){
QRadialGradient gradient(point,mDiffraction);
gradient.setColorAt(0,QColor(255,0,0,mAlpha));
gradient.setColorAt(1,QColor(0,0,0,0));
dataPainter.setBrush(gradient);
dataPainter.drawEllipse(point,mDiffraction,mDiffraction);
}
transAlphaToColor();
}
根据透明度图各像素点,根据色彩对应表转为颜色热力图:
void MapHeatImage::transAlphaToColor()
{
mHeatImage = QImage(this->width(),this->height(),QImage::Format_ARGB32);
mHeatImage.fill(Qt::transparent);
QPainter heatPainter(&mHeatImage);
for(int r=0;r<alphaImage.height();r++){
const uchar *line_data = alphaImage.scanLine(r);
QRgb* line_heat = reinterpret_cast<QRgb*>(mHeatImage.scanLine(r));
for(int c=0;c<alphaImage.width();c++){
if(mHideZeroPoint){
if(line_data[c]>0)
line_heat[c] = colorList[line_data[c]];
}else{
line_heat[c] = colorList[line_data[c]];
}
}
}
update();
}
将控件中绘制得到的热力图:
void MapHeatImage::paint(QPainter *painter)
{
painter->drawImage(QPoint(0,0),mHeatImage);
if(mLegendVisible){
painter->drawImage(QPoint(10,10),mColorImage);
}
}
关于热力图在QML地图中使用的Demo可转到我博客中:
Qt QML地图上绘制热力图(Qt/QML组件)https://blog.csdn.net/zjgo007/article/details/121687451?spm=1001.2014.3001.5501https://blog.csdn.net/zjgo007/article/details/121687451?spm=1001.2014.3001.5501完整示例GitHub:https://github.com/zjgo007/QmlDemo/tree/master/HeatDemohttps://github.com/zjgo007/QmlDemo/tree/master/HeatDemo