前文链接:QGraphicsView实现简易地图4『局部加载-地图漫游』
由于GCJ02 Web 墨卡托投影 纬度并不随像素等分,且两极跨度较大,因此本次演示采用的经纬网等分逻辑为等分像素。同等像素跨度之间,两级纬度变化较小,越靠近赤道附近纬度变化越大。以下将提供实现此需求的核心代码。
1、动态演示效果
2、静态展示图片
核心代码
void MapView::showGraticules()
{
// 计算等分像素后的经纬度步长
int gridCount = MapUtility::graticulesGridCount(m_curLevel);
int mapSideCount = apUtility::mapSideCount(m_curLevel);
double perLon = PIXMAP_SIZE * mapSideCount * 1.0 / gridCount;
double perLat = perLon;
// 计算呈现的瓦片地图左上角和右下角的场景坐标
QPoint topLeftScenePos(m_topLeftTileCoord.x * PIXMAP_SIZE, m_topLeftTileCoord.y * PIXMAP_SIZE);
QPoint bottomRightScenePos((m_bottomRightTileCoord.x + 1) * PIXMAP_SIZE, (m_bottomRightTileCoord.y + 1) * PIXMAP_SIZE);
// 计算经纬线覆盖范围,此处采用的逻辑是经纬网覆盖区域>=呈现的瓦片地图区域
int leftGridIndex = qFloor(topLeftScenePos.x() / perLon);
int rightGridIndex = qCeil(bottomRightScenePos.x() / perLon);
int topGridIndex = qFloor(topLeftScenePos.y() / perLat);
int bottomGridIndex = qCeil(bottomRightScenePos.y() / perLat);
if (leftGridIndex < 0)
leftGridIndex = 0;
if (rightGridIndex > gridCount)
rightGridIndex = gridCount;
if (topGridIndex < 0)
topGridIndex = 0;
if (bottomGridIndex > gridCount)
bottomGridIndex = gridCount;
// 视口宽度和高度
int vw = viewport()->width();
int vh = viewport()->height();
// 场景宽度和高度
int sw = MapUtility::sceneSize(m_curLevel);
int sh = sw;
// 视口右下角对应场景坐标
QPointF bottomRightViewToScenePos = mapToScene(viewport()->rect().bottomRight());
// 经纬网线条颜色、文本颜色
QColor gridLineColor(255, 163, 70);
QColor textColor(Qt::white);
// 绘制经纬网:纬度线
for (int row = topGridIndex; row <= bottomGridIndex; ++row)
{
// 纬度线
double sceneY = row * perLat;
QGraphicsLineItem *item = m_scene->addLine(topLeftScenePos.x(), sceneY, bottomRightScenePos.x(), sceneY);
item->setPen(QPen(gridLineColor, 1, Qt::DotLine));
item->setZValue(50);
m_vecGraticulesItems.append(item);
// 纬度文本
double lat = MapUtility::latFromSceneY(sceneY, m_curLevel);
QGraphicsTextItem *textItem = m_scene->addText(CommonUtility::convertToDMSLatSymbol(lat));
textItem->setDefaultTextColor(Qt::white);
QFont font = textItem->font();
font.setFamily("微软雅黑");
textItem->setFont(font);
// 调整文本位置
QRectF textBoundingRect = textItem->boundingRect();
int sceneX = sw <= vw ? bottomRightScenePos.x() : bottomRightViewToScenePos.x();
textItem->setPos(sceneX - textItem->boundingRect().width(), sceneY - textBoundingRect.height() / 2);
m_vecGraticulesTextItems.append(textItem);
}
// 绘制经纬网:经度线
for (int col = leftGridIndex; col <= rightGridIndex; ++col)
{
// 经度线
double sceneX = col * perLon;
QGraphicsLineItem *item = m_scene->addLine(sceneX, topLeftScenePos.y(), sceneX, bottomRightScenePos.y());
item->setPen(QPen(gridLineColor, 1, Qt::DotLine));
item->setZValue(50);
m_vecGraticulesItems.append(item);
// 经度文本
double lon = MapUtility::lonFromSceneX(sceneX, m_curLevel);
QGraphicsTextItem *textItem = m_scene->addText(CommonUtility::convertToDMSLonSymbol(lon));
textItem->setDefaultTextColor(Qt::white);
QFont font = textItem->font();
font.setFamily("微软雅黑");
textItem->setFont(font);
// 调整文本位置
QRectF textBoundingRect = textItem->boundingRect();
int sceneY = sh <= vh ? bottomRightScenePos.y() : bottomRightViewToScenePos.y();
textItem->setPos(sceneX - textBoundingRect.width() / 2, sceneY - textItem->boundingRect().height());
m_vecGraticulesTextItems.append(textItem);
}
}
辅助代码
void CommonUtility::convertToDMS(double value, int &d, int &m, int &s)
{
d = (int)(value);
m = (int)((value - d) * 60);
s = (int)(((value - d) * 60 - m) * 60);
// 四舍五入
float e = ((value - d) * 60 - m) * 60 - s;
if (5 <= (int)(e * 10))
s += 1;
// 秒进位
if (60 == s)
{
s = 0;
m += 1;
}
// 分进位
if (60 == m)
{
m = 0;
d += 1;
}
}
QString CommonUtility::convertToDMS(double value)
{
int d, m, s;
convertToDMS(value, d, m, s);
QString strM = QString::number(m).rightJustified(2, '0');
QString strS = QString::number(s).rightJustified(2, '0');
return QString("%1°%2′%3″").arg(d).arg(strM).arg(strS);
}
QString CommonUtility::convertToDMSLonSymbol(double value)
{
return QString("%1%2").arg(convertToDMS(fabs(value))).arg(value > 0 ? "E" : (value != 0 ? "W" : ""));
}
QString CommonUtility::convertToDMSLatSymbol(double value)
{
return QString("%1%2").arg(convertToDMS(fabs(value))).arg(value > 0 ? "N" : (value != 0 ? "S" : ""));
}