目录
前言
最近接触到一个项目,就是根据导入的矢量点文件以瓦片渲染的方式生成热力图。
一、整体思路
当地图请求某张瓦片时,根据瓦片范围查询矢量文件在该范围的点,再用点的经纬度计算出该点在这张瓦片上的像素坐标,以该像素坐标用alpha通道绘制渐变圆,其余地方以alpha=0进行填充,此时得到的是一张灰度图像,再根据alpha值映射到一条色带上进行赋色,即可得到热力图像。
二、实现步骤
1.主要代码
代码如下(示例):
E_QUERY_RESULT CGDALHeatmapFieldDataSource::QueryImage(QImage& rImage, const CTileKey& rTileKey) const
{
CTileKey tileKey = rTileKey; //当前瓦片
//设置当前瓦片的投影
E_TILE_MODEL eTileModel = tileKey.GetTileModel();
switch(eTileModel)
{
case E_TILE_MODEL_ACMAP:
{
bool bWGS84 = Tools::isWGS(m_eMapType);
if (bWGS84)
{
eTileModel = E_TILE_MODEL_WGS84;
}
else
{
eTileModel = E_TILE_MODEL_MERCATOR;
}
}
break;
case E_TILE_MODEL_MERCATOR:
{
}
break;
case E_TILE_MODEL_WGS84:
{
}
break;
}
CTileModel* ptileModel = ITileModel::GetTileModel(eTileModel);
if (ptileModel == NULL)
{
return E_QUERY_RESULT_NODATA;
}
tileKey.ChangeTileModel(eTileModel);
EXTENT rExtent = ptileModel->GetGeoExtent(tileKey);
//瓦片坐上角坐标转像素坐标
int nTilePixelX;
int nTilePixelY;
ptileModel->GetPixelCoordinate(nTilePixelX, nTilePixelY, rTileKey);
//用一张瓦片大小的图片把alpha通道填充为全0
QImage img(256,256,QImage::Format_Alpha8);
img.fill(Qt::transparent);
img.fill(QColor(0,0,0,0));
QPainter painter(&img);
//painter.save();
//把瓦片左上角像素坐标转为世界坐标即整个地图左上角
painter.translate(-nTilePixelX, -nTilePixelY);
double dPixelX = 0;
double dPixelY = 0;
for (int i=0; i<m_vec3D.size(); i++)
{
//计算出处于当前瓦片内的矢量点集对应的像素坐标(世界坐标)
ptileModel->GetPixelViaGeo(dPixelX, dPixelY, tileKey.GetLevel(), m_vec3D.at(i).x, m_vec3D.at(i).y);
//设置渐变
QRadialGradient gradient(dPixelX, dPixelY, m_nRadius);//设置渐变模式为径向m_nRadius为渐变半径
//考虑是否加权两种情况,不加劝最大透明度即为当前设置的透明度,否则为加权后的透明度
if(m_fieldName == "NULL" || m_vecAlphaValue.empty())
{
gradient.setColorAt(0, QColor(0, 0, 0, nMaxAlphaValue));//设置圆心为最大透明度(nMaxAlphaValue)
}
else
{
gradient.setColorAt(0, QColor(0, 0, 0, m_vecAlphaValue.at(i)));//设置圆心为最大透明度(m_vecAlphaValue.at(i))
}
gradient.setColorAt(1, QColor(0, 0, 0, 0));
painter.setPen(Qt::NoPen);
painter.setBrush(gradient);
//抗锯齿效果
painter.setRenderHint(QPainter::HighQualityAntialiasing);
//绘制渐变圆
painter.drawEllipse(QPoint(dPixelX, dPixelY), m_nRadius, m_nRadius);
//painter.save();
//painter.restore();
}
//因为没有色带接口,该块主要用于自定义一个渐变色带
QLinearGradient gradientLine(0, 0, nMaxAlphaValue+1, 1);
//保证色带最大值始终对应设置的alpha最大值
QImage imgCanvas(nMaxAlphaValue+1, 1, QImage::Format_RGBA8888);
gradientLine.setColorAt(0.45, Qt::blue);
gradientLine.setColorAt(0.55, Qt::cyan);
gradientLine.setColorAt(0.65, Qt::green);
gradientLine.setColorAt(0.85, Qt::yellow);
gradientLine.setColorAt(1.0, Qt::red);
QPainter painter1(&imgCanvas);
painter1.setBrush(gradientLine);
painter1.setPen(Qt::NoPen);
painter1.fillRect(imgCanvas.rect(), gradientLine);
//像素转换把灰度图转换为有颜色的图像
int alpha = 0;
int finalAlpha = 0;
QColor color;
QImage img1(NUM_DEFAULT_PIXEL_SIZE, NUM_DEFAULT_PIXEL_SIZE, QImage::Format_RGBA8888);//NUM_DEFAULT_PIXEL_SIZE = 256
for (int i = 0; i < 256; ++i)
{
for (int j = 0; j < 256; ++j)
{
alpha = qAlpha(img.pixel(i, j));
//alpha等于0即为透明还是设为透明
if (!alpha)
{
img1.setPixel(i, j, qRgba(0,
0,
0,
0));
continue;
}
//因为存在渐变圆透明度叠加,需保证透明度不超过设置的最大值
finalAlpha = (alpha < nMaxAlphaValue ? alpha : nMaxAlphaValue);
//根据透明度从自定义色带取对应的值进行赋色
color = imgCanvas.pixel(finalAlpha,0.5);
img1.setPixel(i, j, qRgba(color.red(),
color.green(),
color.blue(),
finalAlpha));
}
}
rImage = img1;
//}
/*if (!rImage.isNull())
{
std::stringstream ss;
ss << "D:/SGDownload/" << rTileKey.GetTileKeyString() << ".png";
rImage.save(ConvertStdStringToQString(ss.str()), "png", -1);
}*/
return E_QUERY_RESULT_VALID;
}
2.具体效果
不加权:
加权:
更改渲染半径:
更改透明度:
总结
分享结束!