










前期投入大量精力,基于纯QWidget和Painter绘制了一套电子地图控件。该控件已具备成熟的多级图层叠加能力,底层支持天地图、高德等瓦片地图,顶层图形绘制层可灵活渲染折线、多边形、矩形及圆形等覆盖物。
近期针对社区高频询问的“海图支持”需求,完成了核心功能的研发。在数据源方面,考虑到部分厂商的限制,最终选用了免费且体验极佳的天地图作为海图瓦片源。其不仅采用标准的GPS坐标系,卫星图更支持至18级,且瓦片下载后支持离线部署。
为适配在线海图瓦片,本次对底层控件进行了深度重构:
投影系解耦:打破原有单一的墨卡托投影限制,支持为每个控件实例独立设置投影类型,完美兼容经纬度投影。
瓦片尺寸兼容:在原有256像素标准的基础上,新增对512像素瓦片的渲染支持。
经过多轮测试与调整,目前控件已实现天地图海图的无缝叠加。同时支持任意底图组合,例如构建“矢量底图 + 海图 + 矢量标记/路网图”的三层叠加架构,大幅提升了地图展示的完整性与专业度。

#include "layermapadapter.h"
#include "imagemanager.h"
#include "imagehelper.h"
LayerMapAdapter::LayerMapAdapter(const QString &name, MapAdapter *mapadapter, int min_zoom, int max_zoom, QObject *parent)
: LayerBase(name, LayerType_MapAdapter, min_zoom, max_zoom, parent),
m_bgColor(Qt::transparent),
m_mapadapter(mapadapter)
{
m_image_manager = NULL;
}
LayerMapAdapter::~LayerMapAdapter()
{
}
MapAdapter *LayerMapAdapter::mapAdapter()
{
QReadLocker locker(&m_mutex);
return m_mapadapter;
}
void LayerMapAdapter::setMapAdapter(MapAdapter *mapadapter)
{
{
QWriteLocker locker(&m_mutex);
m_mapadapter = mapadapter;
}
emit requestRedraw();
}
void LayerMapAdapter::mouseEvent(MapControl *map_control, int mouse_type, const PointWorldCoord &mouse_coord, int cur_zoom) const
{
}
void LayerMapAdapter::drawXYZ(QPainter *painter, const QPointF &point, int x, int y, int z) const
{
//绘制对应xyz值
QTextOption option;
option.setAlignment(Qt::AlignCenter);
option.setWrapMode(QTextOption::WordWrap);
QString text = QString("x=%1\ny=%2\nz=%3").arg(x).arg(y).arg(z);
QRectF rect(point.x(), point.y(), m_tile_size, m_tile_size);
painter->save();
QFont font = painter->font();
font.setPixelSize(50);
painter->setFont(font);
painter->setPen(Qt::red);
painter->drawText(rect, text, option);
painter->drawRect(rect);
painter->restore();
}
void LayerMapAdapter::draw(QPainter *painter, const RectWorldPx &backbuffer_rect_px, int cur_zoom) const
{
QReadLocker locker(&m_mutex);
//没有设置或者不可见则不用继续
if (!m_mapadapter || !m_image_manager || !isVisible(cur_zoom)) {
return;
}
//没有设置瓦片地址也不用继续
if (m_mapadapter->url().isEmpty()) {
return;
}
//绘制背景颜色/形成样式风格
if (m_bgColor != Qt::transparent) {
painter->fillRect(backbuffer_rect_px.rawRect(), m_bgColor);
}
//计算瓦片绘制区域
QSize tile_size(m_tile_size, m_tile_size);
int furthest_tile_left = qFloor(backbuffer_rect_px.leftPx() / tile_size.width());
int furthest_tile_top = qFloor(backbuffer_rect_px.topPx() / tile_size.height());
int furthest_tile_right = qFloor(backbuffer_rect_px.rightPx() / tile_size.width());
int furthest_tile_bottom = qFloor(backbuffer_rect_px.bottomPx() / tile_size.height());
//绘制可视区域内的瓦片
for (int i = furthest_tile_left; i <= furthest_tile_right; ++i) {
for (int j = furthest_tile_top; j <= furthest_tile_bottom; ++j) {
if (!m_mapadapter->isTileValid(i, j, cur_zoom)) {
continue;
}
PointWorldPx top_left_px(i * tile_size.width(), j * tile_size.height());
QPointF point = top_left_px.rawPoint();
QPixmap pixmap = m_image_manager->getImage(m_mapadapter->tileQuery(i, j, cur_zoom));
//可能只是去请求了瓦片/而并没有真正获取到图片
//可能需要缩放图片/256像素的地图/请求的512像素的瓦片
if (!pixmap.isNull()) {
if (pixmap.size() != tile_size) {
pixmap = pixmap.scaled(tile_size);
//pixmap = pixmap.scaled(tile_size, Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
}
painter->drawPixmap(point, pixmap);
}
if (ImageHelper::drawTileXYZ) {
this->drawXYZ(painter, point, i, j, cur_zoom);
}
}
}
//计算可视区域外的范围
//预加载可视区域外的瓦片/四周不可见区域/默认会预加载四周的/方便拖动快速绘制
int prefetch_tile_offset = 0;
int prefetch_tile_left = furthest_tile_left - prefetch_tile_offset;
int prefetch_tile_top = furthest_tile_top - prefetch_tile_offset;
int prefetch_tile_right = furthest_tile_right + prefetch_tile_offset;
int prefetch_tile_bottom = furthest_tile_bottom + prefetch_tile_offset;
//预加载可视区域外上侧和下侧部分
for (int i = prefetch_tile_left; i <= prefetch_tile_right; ++i) {
if (m_mapadapter->isTileValid(i, prefetch_tile_top, cur_zoom)) {
m_image_manager->prefetchImage(m_mapadapter->tileQuery(i, prefetch_tile_top, cur_zoom));
}
if (m_mapadapter->isTileValid(i, prefetch_tile_bottom, cur_zoom)) {
m_image_manager->prefetchImage(m_mapadapter->tileQuery(i, prefetch_tile_bottom, cur_zoom));
}
}
//预加载可视区域外左侧和右侧部分
for (int j = prefetch_tile_top; j <= prefetch_tile_bottom; ++j) {
if (m_mapadapter->isTileValid(prefetch_tile_left, j, cur_zoom)) {
m_image_manager->prefetchImage(m_mapadapter->tileQuery(prefetch_tile_left, j, cur_zoom));
}
if (m_mapadapter->isTileValid(prefetch_tile_right, j, cur_zoom)) {
m_image_manager->prefetchImage(m_mapadapter->tileQuery(prefetch_tile_right, j, cur_zoom));
}
}
}
void LayerMapAdapter::setBgColor(const QColor &bgColor)
{
if (m_bgColor != bgColor) {
m_bgColor = bgColor;
emit requestRedraw();
}
}
void LayerMapAdapter::setImageManager(ImageManager *imageManager)
{
this->m_image_manager = imageManager;
}
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。