










从来没有想过,明明已经有个各种js版本的地图组件,为何还要自讨苦吃底层全部实现一遍,其实呢,这也完全是被逼的,因为有客户强烈需求,比如有些客户需要在任意平台实现这个组件,比如安卓上甚至编译成wasm运行在网页上,而之前的js版本的,强烈依赖浏览器控件,而手机端只有qml才有浏览器组件,wasm端qml的浏览器控件也不可用,尽管可以通过quickwidget加载qml进行交互,总归还是很别扭。最核心的一个难点来了,那就是某些国产的机器,性能配置很低,跑浏览器很悬,包括一些开发板,老板只是想用来显示一个地图,上面放两个标注点即可,为了这个功能,引入一个这么大的浏览器控件,得不偿失,毕竟浏览器号称吃内存大户,板子就几百兆内存最多了,哪里够吃的啊,于是迫切需要一个纯qwidget代码绘制的地图组件,说干就干应运而生。通过各种大量的对比测试,性能暴增几十倍不止,尤其是大量的标注点和轨迹运动的时候。

#include "frmoverlay.h"
#include "ui_frmoverlay.h"
#include "qthelper.h"
#include "maphelper.h"
#include "maputil.h"
#include "overlayhelper.h"
#include "magicfish.h"
frmOverlay::frmOverlay(QWidget *parent) : QWidget(parent), ui(new Ui::frmOverlay)
{
ui->setupUi(this);
this->initForm();
this->initConfig();
this->loadBoundary();
this->loadMap();
}
frmOverlay::~frmOverlay()
{
delete ui;
}
void frmOverlay::initForm()
{
point = QPointF(121.424362, 31.175942);
ui->cboxPixmap->setCurrentIndex(1);
ui->mapWidget->setFlag("frmOverlay");
connect(ui->cboxFlag->lineEdit(), SIGNAL(textChanged(QString)), this, SLOT(textChanged(QString)));
connect(ui->mapWidget, SIGNAL(mapEvent(int, bool, QPointF)), this, SLOT(mapEvent(int, bool, QPointF)));
}
void frmOverlay::initConfig()
{
MapHelper::loadTileSource(ui->cboxTileSource, AppConfig::OverlaySource);
connect(ui->cboxTileSource, SIGNAL(currentIndexChanged(int)), this, SLOT(saveConfig()));
connect(ui->cboxTileSource, SIGNAL(currentIndexChanged(int)), this, SLOT(loadMap()));
MapHelper::loadTileType(ui->cboxTileType, AppConfig::OverlayType);
connect(ui->cboxTileType, SIGNAL(currentIndexChanged(int)), this, SLOT(saveConfig()));
connect(ui->cboxTileType, SIGNAL(currentIndexChanged(int)), this, SLOT(loadMap()));
ui->cboxOffline->setCurrentIndex(AppConfig::OverlayOffline);
connect(ui->cboxOffline, SIGNAL(currentIndexChanged(int)), this, SLOT(saveConfig()));
connect(ui->cboxOffline, SIGNAL(currentIndexChanged(int)), this, SLOT(loadMap()));
ui->cboxCache->setCurrentIndex(AppConfig::OverlayCache);
connect(ui->cboxCache, SIGNAL(currentIndexChanged(int)), this, SLOT(saveConfig()));
connect(ui->cboxCache, SIGNAL(currentIndexChanged(int)), this, SLOT(loadMap()));
}
void frmOverlay::saveConfig()
{
AppConfig::OverlaySource = ui->cboxTileSource->itemData(ui->cboxTileSource->currentIndex()).toInt();
AppConfig::OverlayType = ui->cboxTileType->itemData(ui->cboxTileType->currentIndex()).toInt();
AppConfig::OverlayOffline = ui->cboxOffline->currentIndex();
AppConfig::OverlayCache = ui->cboxCache->currentIndex();
AppConfig::writeConfig();
}
void frmOverlay::loadBoundary()
{
//加载行政区划边界点文件
QString path = QtHelper::appPath() + "/mapboundary";
QStringList files = QDir(path).entryList(QStringList() << "*.txt");
ui->cboxBoundary->clear();
ui->cboxBoundary->blockSignals(true);
foreach (QString file, files) {
QString name = file;
name.replace(".txt", "");
ui->cboxBoundary->addItem(name, path + "/" + file);
}
ui->cboxBoundary->blockSignals(false);
}
void frmOverlay::reset()
{
index = 1;
flag = "overlay1";
ui->cboxFlag->clear();
ui->cboxFlag->lineEdit()->setText(flag);
}
void frmOverlay::loadMap()
{
this->reset();
int tileSource = ui->cboxTileSource->itemData(ui->cboxTileSource->currentIndex()).toInt();
ui->mapWidget->setTileSource(TileSource(tileSource));
int tileType = ui->cboxTileType->itemData(ui->cboxTileType->currentIndex()).toInt();
ui->mapWidget->setTileType(TileType(tileType));
QString offlinePath = TileHelper::getOfflinePath(TileSource(tileSource));
ui->mapWidget->setOffline(ui->cboxOffline->currentIndex() == 1);
ui->mapWidget->setOfflinePath(offlinePath);
QString cachePath = (ui->cboxCache->currentIndex() == 1 ? QtHelper::appPath() + "/mapcache" : "");
ui->mapWidget->setCachePath(cachePath);
ui->mapWidget->load();
}
void frmOverlay::mapEvent(int type, bool right, const QPointF &point)
{
if (type == MouseType_Press && !right) {
this->point = point;
MapHelper::setLngLat(point, ui->txtLng, ui->txtLat);
}
}
void frmOverlay::addIndex()
{
index++;
QString text = QString("overlay%1").arg(index);
QString item = QString("overlay%1").arg(index - 1);
ui->cboxFlag->addItem(item);
ui->cboxFlag->lineEdit()->setText(text);
}
void frmOverlay::textChanged(const QString &arg1)
{
if (!arg1.isEmpty()) {
this->flag = arg1;
}
}
void frmOverlay::on_btnAddLabel_clicked()
{
ui->mapWidget->addLabel(flag, point, "#ff0000", "测试文本_" + flag, 2, 20, 0);
this->addIndex();
}
void frmOverlay::on_btnAddMarker_clicked()
{
ui->mapWidget->addMarker(flag, point, ":/mapimage/marker_mini.png", QPixmap());
this->addIndex();
}
void frmOverlay::on_btnAddPolyline_clicked()
{
QVector<QPointF> points = MapHelper::stringToPoints("121.428961,31.249075|121.557167,31.213504|121.469780,31.135397|121.403090,31.198678");
qreal degrees = MapUtil::getDegreesPerPixel(ui->mapWidget->getZoom());
points = MapHelper::getRandPoints(point, 4, degrees * 150);
ui->mapWidget->addPolyline(flag, points, "#A279C5", 3);
this->addIndex();
}
void frmOverlay::on_btnAddPolygon_clicked()
{
QVector<QPointF> points = MapHelper::stringToPoints("121.428961,31.249075|121.557167,31.213504|121.469780,31.135397|121.403090,31.198678");
qreal degrees = MapUtil::getDegreesPerPixel(ui->mapWidget->getZoom());
points = MapHelper::getRandPoints(point, 3, degrees * 150);
ui->mapWidget->addPolygon(flag, points, "#00ff00", 2, QColor(0, 255, 0, 100));
this->addIndex();
}
void frmOverlay::on_btnAddRectangle_clicked()
{
QPointF topLeft = QPointF(121.266260, 31.202138);
QPointF bottomRight = QPointF(121.370319, 31.131935);
//根据当前缩放对应的距离/随机生成一定像素的尺寸的矩形
qreal degrees = MapUtil::getDegreesPerPixel(ui->mapWidget->getZoom());
qreal width = (rand() % 70 + 50) * degrees;
qreal height = (rand() % 50 + 30) * degrees;
topLeft = point;
bottomRight = QPointF(topLeft.x() + width, topLeft.y() + height);
ui->mapWidget->addRectangle(flag, topLeft, bottomRight, "#008888", 2, QColor(0, 255, 0, 100));
this->addIndex();
}
void frmOverlay::on_btnAddCircle_clicked()
{
//根据当前缩放级别获取合适的半径/这里演示随机生成像素大小
int px = rand() % 50 + 30;
qreal radius = px * MapUtil::getDegreesPerPixel(ui->mapWidget->getZoom());
ui->mapWidget->addCircle(flag, point, radius, "#0000ff", 3, QColor(0, 0, 255, 50));
this->addIndex();
}
void frmOverlay::on_btnAddWidget_clicked()
{
MagicFish *widget = new MagicFish(ui->mapWidget->getMapControl());
ui->mapWidget->addWidget(flag, point, widget);
this->addIndex();
}
void frmOverlay::on_btnUpdateOverlay_clicked()
{
//找到对应标识图形
OverlayBase *overlay = ui->mapWidget->getOverlay(flag);
if (!overlay) {
return;
}
//找到对应图形的类型
OverlayType type = overlay->overlayType();
if (type == OverlayType_Label) {
ui->mapWidget->updateLabel(flag, point, "#00ffff", "旋转文字\n换行文字测试", 2, 50, 30);
} else if (type == OverlayType_Marker) {
ui->mapWidget->updateMarker(flag, point, ":/mapimage/fly.png", QPixmap(), 45, 2);
} else if (type == OverlayType_Polyline) {
ui->mapWidget->updatePolyline(flag, QStringList(), QColor(0, 100, 100, 180), 5);
} else if (type == OverlayType_Polygon) {
ui->mapWidget->updatePolygon(flag, QStringList(), Qt::yellow, 4, QColor(250, 200, 89, 150));
} else if (type == OverlayType_Rectangle) {
ui->mapWidget->updateRectangle(flag, QPointF(), QPointF(), Qt::red, 3, QColor(255, 0, 89, 150));
} else if (type == OverlayType_Circle) {
//占据100个固定像素
qreal radius = 100 * MapUtil::getDegreesPerPixel(ui->mapWidget->getZoom());
//也可以传入距离获取半径
//qreal radius = MapUtil::getDegreesFromMeters(15000);
ui->mapWidget->updateCircle(flag, point, radius, "#ff0000");
} else if (type == OverlayType_Widget) {
ui->mapWidget->updateWidget(flag, point);
}
}
void frmOverlay::on_btnDeleteOverlay_clicked()
{
QString flag = ui->cboxFlag->currentText();
ui->mapWidget->deleteOverlay(flag, true);
//同时移除标识下拉框中对应标识
ui->cboxFlag->removeItem(ui->cboxFlag->findText(flag));
if (ui->cboxFlag->count() == 0) {
this->reset();
}
}
void frmOverlay::on_btnDeleteRectangle_clicked()
{
ui->mapWidget->deleteGroup("rectangle", true);
}
void frmOverlay::on_btnDeleteCircle_clicked()
{
ui->mapWidget->deleteGroup("circle", true);
}
void frmOverlay::on_btnClearOverlay_clicked()
{
this->reset();
ui->mapWidget->clearOverlay();
}
void frmOverlay::on_btnSetOverlayVisible_clicked()
{
if (ui->btnSetOverlayVisible->text() == "显示图形") {
ui->mapWidget->setOverlayVisible(flag, true);
ui->btnSetOverlayVisible->setText("隐藏图形");
} else {
ui->mapWidget->setOverlayVisible(flag, false);
ui->btnSetOverlayVisible->setText("显示图形");
}
}
void frmOverlay::on_btnSetLayerVisible_clicked()
{
if (ui->btnSetLayerVisible->text() == "显示图层") {
ui->mapWidget->setLayerVisible(true);
ui->btnSetLayerVisible->setText("隐藏图层");
} else {
ui->mapWidget->setLayerVisible(false);
ui->btnSetLayerVisible->setText("显示图层");
}
}
void frmOverlay::on_btnSetOverlayZIndex_clicked()
{
ui->mapWidget->setOverlayZIndex(flag, ui->cboxZIndex->currentText().toInt());
}
void frmOverlay::on_btnSavePixmap_clicked()
{
//优先行政区划/其次添加的多边形/都没有则指定的一个区域
QVector<QPointF> points = ui->mapWidget->getPolygonPoints("boundary");
if (points.count() == 0) {
points = ui->mapWidget->getPolygonPoints(flag);
}
if (points.count() == 0) {
points << MapHelper::stringToPoints("121.428961,31.249075|121.557167,31.213504|121.469780,31.135397|121.403090,31.198678");
}
int index = ui->cboxPixmap->currentIndex();
QString fileName = QString("%1/mapsnap/%2.jpg").arg(QtHelper::appPath()).arg(STRDATETIME);
if (index == 0) {
ui->mapWidget->savePixmap(fileName, false);
} else if (index == 1) {
ui->mapWidget->savePixmap(fileName, true);
} else if (index == 2) {
ui->mapWidget->savePixmap(fileName, false, points);
} else if (index == 3) {
//QRectF rect(115.428, 41.0658, 2.09522, -1.61602);
QRectF rect(QPointF(115.428, 41.0658), QPointF(117.52322, 39.44978));
MapHelper::savePixmap(fileName, rect);
}
}
void frmOverlay::on_btnAddBoundary_clicked()
{
QString fileName = QtHelper::getOpenFileName("*.txt", QtHelper::appPath() + "/mapboundary");
if (!fileName.isEmpty()) {
QFileInfo file(fileName);
ui->cboxBoundary->addItem(file.baseName(), fileName);
ui->cboxBoundary->setCurrentIndex(ui->cboxBoundary->count() - 1);
}
}
void frmOverlay::on_cboxBoundary_currentIndexChanged(int index)
{
on_btnClearOverlay_clicked();
QString fileName = ui->cboxBoundary->itemData(index).toString();
QString points = MapHelper::readFile(fileName);
ui->mapWidget->addBoundary("boundary", points, "#ff55ff", 2, QColor(255, 100, 255, 100));
}
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。