一、前言说明
搞地图软件开发,在联网的环境,一般会采用直接联网拉取瓦片地图文件,有很多场景是没有网络的环境,这就需要先把一张张的瓦片地图文件下载下来,在通过固定的路径格式,加载到软件中,关于离线地图下载这块,网上的文章不要太多,而且都有一定的参考价值,核心就是搞到一个瓦片的地址即可,而且不同地图厂商提供的地址格式不一样,甚至有些还要做一定的运算,比如腾讯地图,最麻烦的就是百度地图,用的不是国家标准也不是国际标准的坐标系,所以现在通用的地图软件都不大喜欢接入百度地图瓦片。现在AI这么牛逼,基本上问问也能找到合适的地址。
之前就已经实现了各大地图厂家的离线地图下载,比如百度地图、高德地图、天地图、腾讯地图、谷歌地图等,基本上就是按照当前软件展示的区域来下载,近期有个用户需求是希望能自己选择一块区域,然后下载这块指定的区域的地图即可,这样可以大量节约下载的时间,毕竟瓦片文件的数量可以少很多,如何实现呢?其实就是让用户在地图上画一个矩形区域,然后立即获取这个矩形区域的经纬度坐标区域,然后根据这个区域大小计算出对应的瓦片即可。
二、效果图
三、相关代码
#include "frmmapdownload.h"
#include "ui_frmmapdownload.h"
#include "qthelper.h"
#include "webview.h"
#include "maphelper.h"
#include "maputil.h"
#include "mapdownload.h"
#include "mapdownhelper.h"
#define zoomMin 1 //表示开始的级别/一般来说1-4级都只有1张图而且很可能是黑的/建议从5级别开始
#define zoomMax 20 //表示最大的级别/百度地图到18/大城市到19/谷歌地图到20/特大城市可以到22
frmMapDownload::frmMapDownload(QWidget *parent) : QWidget(parent), ui(new Ui::frmMapDownload)
{
ui->setupUi(this);
this->initForm();
this->initConfig();
this->initTable();
}
frmMapDownload::~frmMapDownload()
{
if (ui->btnStart->text() == "停止") {
on_btnStart_clicked();
}
delete ui;
}
void frmMapDownload::showEvent(QShowEvent *)
{
//只需要加载一次/避免重复初始化
static bool isLoad = false;
if (!isLoad) {
isLoad = true;
QMetaObject::invokeMethod(this, "loadMap", Qt::QueuedConnection);
}
}
void frmMapDownload::initForm()
{
//设置右侧固定宽度
ui->widgetRight->setFixedWidth(AppData::RightWidth);
ui->tabWidget->setCurrentIndex(0);
//实例化地图下载类并关联信号
mapDown = new MapDownload(this);
connect(mapDown, SIGNAL(finsh(int)), this, SLOT(finsh(int)));
connect(mapDown, SIGNAL(finsh(int, int, int, int, bool)), this, SLOT(finsh(int, int, int, int, bool)));
//实例化浏览器控件并加入到布局
mapObj = NULL;
webView = new WebView(this);
webView->setLayout(ui->gridLayout);
//关联浏览器控件信号
connect(webView, SIGNAL(loadSuccess()), this, SLOT(loadSuccess()), Qt::QueuedConnection);
connect(webView, SIGNAL(receiveDataFromJs(QString, QVariant)), this, SLOT(receiveDataFromJs(QString, QVariant)));
}
void frmMapDownload::initConfig()
{
MapCore mapCore = (MapCore)AppConfig::DownLoadCore;
MapHelper::loadMapCore(ui->cboxMapCore, AppConfig::DownLoadCore);
//重新纠正当前下拉框对应的内核
mapCore = (MapCore)AppConfig::DownLoadCore;
connect(ui->cboxMapCore, SIGNAL(currentIndexChanged(int)), this, SLOT(saveConfig()));
connect(ui->cboxMapCore, SIGNAL(currentIndexChanged(int)), this, SLOT(loadMap()));
MapHelper::loadDownType(ui->cboxDownType, mapCore);
ui->cboxDownType->setCurrentIndex(AppConfig::DownLoadType);
connect(ui->cboxDownType, SIGNAL(currentIndexChanged(int)), this, SLOT(saveConfig()));
ui->cboxMapArea->setCurrentIndex(AppConfig::DownLoadArea);
on_cboxMapArea_currentIndexChanged(AppConfig::DownLoadArea);
connect(ui->cboxMapArea, SIGNAL(currentIndexChanged(int)), this, SLOT(saveConfig()));
ui->cboxCityName->lineEdit()->setText(AppConfig::DownLoadCity);
connect(ui->cboxCityName->lineEdit(), SIGNAL(textChanged(QString)), this, SLOT(saveConfig()));
ui->sboxThread->setValue(AppConfig::DownLoadThread);
connect(ui->sboxThread, SIGNAL(valueChanged(int)), this, SLOT(saveConfig()));
ui->txtPath->setText(MapHelper::getTilePath(mapCore, 0));
}
void frmMapDownload::saveConfig()
{
int downLoadCore = ui->cboxMapCore->itemData(ui->cboxMapCore->currentIndex()).toInt();
if (AppConfig::DownLoadCore != downLoadCore) {
AppConfig::DownLoadCore = downLoadCore;
MapHelper::loadDownType(ui->cboxDownType, (MapCore)AppConfig::DownLoadCore);
}
AppConfig::DownLoadType = ui->cboxDownType->currentIndex();
AppConfig::DownLoadArea = ui->cboxMapArea->currentIndex();
AppConfig::DownLoadCity = ui->cboxCityName->lineEdit()->text().trimmed();
AppConfig::DownLoadThread = ui->sboxThread->value();
AppConfig::writeConfig();
}
void frmMapDownload::initTable()
{
QList<QString> columnNames;
QList<int> columnWidths;
columnNames << "级别" << "瓦片数" << "进度";
columnWidths << 70 << 70 << 30;
int columnCount = columnNames.count();
ui->tableWidget->setColumnCount(columnCount);
ui->tableWidget->setHorizontalHeaderLabels(columnNames);
for (int i = 0; i < columnCount; ++i) {
ui->tableWidget->setColumnWidth(i, columnWidths.at(i));
}
QtHelper::initTableView(ui->tableWidget, AppData::RowHeight);
ui->tableWidget->setStyleSheet("QCheckBox{padding:0px 0px 0px 6px;}");
//添加数据
ui->tableWidget->setRowCount(zoomMax - zoomMin + 1);
for (int zoom = zoomMin; zoom <= zoomMax; zoom++) {
int index = zoom - zoomMin;
QCheckBox *ck = new QCheckBox;
ck->setText(QString("%1级").arg(zoom, 2, 10, QChar('0')));
cks << ck;
ui->tableWidget->setCellWidget(index, 0, ck);
QLabel *lab = new QLabel;
lab->setAlignment(Qt::AlignCenter);
lab->setText("0");
labs << lab;
ui->tableWidget->setCellWidget(index, 1, lab);
QProgressBar *bar = new QProgressBar;
bar->setAlignment(Qt::AlignCenter);
//bar->setFormat("%v");
bars << bar;
ui->tableWidget->setCellWidget(index, 2, bar);
pointLeftBottoms << QPoint(0, 0);
pointRightTops << QPoint(0, 0);
}
}
void frmMapDownload::loadMap()
{
//根据不同地图内核实例化地图类
MapCore mapCore = (MapCore)ui->cboxMapCore->itemData(ui->cboxMapCore->currentIndex()).toInt();
int mapType = ui->cboxDownType->currentIndex();
int zoom = MapHelper::getMapZoom(mapCore, this->objectName());
MapHelper::initMapObj(this, &mapObj, mapCore);
mapObj->setWebView(webView);
mapObj->setSaveFile(SaveFile);
mapObj->setZoom(zoom, zoomMin, zoomMax);
mapObj->setCenterPoint(AppConfig::MapCenter);
if (mapCore == MapCore_Tian) {
mapObj->setMapControl(MapControl_Navigation);
} else {
mapObj->setMapControl(MapControl_Navigation | MapControl_Drawing);
}
//天地图比较特殊有多种地图类型/设置弱属性在那边调用判断
mapObj->setProperty("setMapTypeFull", (mapCore == MapCore_Tian));
mapObj->setMapType(mapType);
mapObj->load();
//设置对应地图内核的离线地图存放目录
ui->txtPath->setText(MapHelper::getTilePath(mapCore, 0));
//当前地图内核不存在该级别的地图则隐藏该级别
int indexLast = zoomMax - zoomMin;
ui->tableWidget->setRowHidden(indexLast, false);
ui->tableWidget->setRowHidden(indexLast - 1, false);
if (MapHelper::isMapBaiDu(mapCore)) {
ui->tableWidget->setRowHidden(indexLast, true);
} else if (mapCore != MapCore_Google) {
ui->tableWidget->setRowHidden(indexLast, true);
ui->tableWidget->setRowHidden(indexLast - 1, true);
}
}
void frmMapDownload::getCount()
{
//计算瓦片数
QString pointLeftBottom = ui->txtPointLeftBottom->text();
QString pointRightTop = ui->txtPointRightTop->text();
QStringList listLeftBottom = pointLeftBottom.split(",");
QStringList listRightTop = pointRightTop.split(",");
double lngLeftBottom = listLeftBottom.at(0).toDouble();
double latLeftBottom = listLeftBottom.at(1).toDouble();
double lngRightTop = listRightTop.at(0).toDouble();
double latRightTop = listRightTop.at(1).toDouble();
//根据不同的地图内核计算不同的范围
MapCore mapCore = (MapCore)ui->cboxMapCore->itemData(ui->cboxMapCore->currentIndex()).toInt();
for (int zoom = zoomMin; zoom <= zoomMax; zoom++) {
QPoint pt1, pt2;
if (MapHelper::isMapBaiDu(mapCore)) {
pt1 = MapUtil::lngLatToTileBaiDu(lngLeftBottom, latLeftBottom, zoom);
pt2 = MapUtil::lngLatToTileBaiDu(lngRightTop, latRightTop, zoom);
} else {
pt1 = MapUtil::lngLatToTile(lngLeftBottom, latLeftBottom, zoom);
pt2 = MapUtil::lngLatToTile(lngRightTop, latRightTop, zoom);
}
//计算XY坐标最大值最小值
int xmin = qMin(pt1.x(), pt2.x());
int xmax = qMax(pt1.x(), pt2.x());
int ymin = qMin(pt1.y(), pt2.y());
int ymax = qMax(pt1.y(), pt2.y());
pt1 = QPoint(xmin, ymin);
pt2 = QPoint(xmax, ymax);
//方便打印查看计算的结果
if (zoom == 25) {
qDebug() << qSetRealNumberPrecision(10) << lngLeftBottom << latLeftBottom << lngRightTop << latRightTop << pt1 << pt2;
}
//当前级别的瓦片数
quint64 count = 0;
for (int j = xmin; j <= xmax; j++) {
for (int k = ymin; k <= ymax; k++) {
count++;
}
}
//显示对应的瓦片总数/设置进度条参数/并更新对应的值
int index = zoom - zoomMin;
if (count > 0 && count < 2147483647) {
bars.at(index)->setRange(0, count);
bars.at(index)->setValue(0);
}
labs.at(index)->setText(QString::number(count));
pointLeftBottoms[index] = pt1;
pointRightTops[index] = pt2;
}
}
void frmMapDownload::clear()
{
//清空计数
MapDownHelper::downloadCount = 0;
redownCount = 0;
redownUrls.clear();
ui->txtTip->setText("等待下载...");
//进度条全部复位
foreach (QProgressBar *bar, bars) {
bar->setValue(0);
}
}
void frmMapDownload::loadSuccess()
{
//禁用双击放大
mapObj->setEnable(EnableType_DoubleClickZoom, false);
on_btnLoad_clicked();
}
void frmMapDownload::runJs(const QString &js)
{
if (mapObj) {
mapObj->runJs(js);
}
}
void frmMapDownload::receiveDataFromJs(const QString &type, const QVariant &data)
{
//qDebug() << TIMEMS << "frmMapDownload" << type << data;
QString result = data.toString();
if (type == "click") {
QString point = MapHelper::getLngLat2(result);
ui->txtPointCenter->setText(point);
} else if (type == "boundary") {
//收到的行政区划返回结果则立即执行
on_btnGet_clicked();
} else if (type == "bounds") {
MapHelper::loadMapBounds(result, ui->txtPointLeftBottom, ui->txtPointRightTop, ui->txtPointCenter, ui->txtZoom);
//自动统计瓦片数
this->getCount();
//滚动条滚到最下面/一般都是需要下载级别大的
ui->tableWidget->scrollToBottom();
} else if (type == "overlaycomplete") {
//选择区域完成后结束绘制模式
this->runJs("doDraw('cancel')");
//获取刚才选择的矩形区域的范围
MapCore mapCore = (MapCore)ui->cboxMapCore->itemData(ui->cboxMapCore->currentIndex()).toInt();
if (MapHelper::isMapBaiDu(mapCore)) {
this->runJs("getOverlayInfo('polygon')");
} else {
this->runJs("getOverlayInfo('rectangle')");
}
} else if (type == "overlayinfo" && (result.startsWith("rectangle|") || result.startsWith("polygon|"))) {
QStringList list = result.split("|");
QString data = list.at(2);
list = data.split(";");
if (list.count() == 4) {
//矩形区域4个坐标对象/顺序依次是左上/右上/右下/左下
ui->txtPointLeftBottom->setText(list.at(3));
ui->txtPointRightTop->setText(list.at(1));
this->receiveDataFromJs("bounds", "");
}
}
}
void frmMapDownload::finsh(int useTime)
{
int hour, min, sec;
MapDownHelper::getTime(useTime, hour, min, sec);
QString text = QString("用时: %1 时 %2 分 %3 秒").arg(hour).arg(min).arg(sec);
ui->txtTip->setText(text);
ui->btnStart->setText("开始");
ui->btnPause->setText("暂停");
ui->widgetPara->setEnabled(true);
ui->btnLoad->setEnabled(true);
ui->btnGet->setEnabled(true);
mapDown->stop();
//统计下载失败张数
int count = redownUrls.count();
#ifdef betaversion
QString msg = QString("试用版最大下载张数 %1 张, 不好意思!").arg(100);
QtHelper::showMessageBoxError(msg);
#else
QString msg = QString("恭喜你全部下载完成, 共下载 %1 张, 失败 %2 张!\n%3").arg(MapDownHelper::downloadCount).arg(count).arg(text);
QtHelper::showMessageBoxInfo(msg);
#endif
if (count > 0) {
qDebug() << TIMEMS << "下载失败图片" << redownUrls;
}
}
void frmMapDownload::finsh(int x, int y, int z, int useTime, bool error)
{
if (z <= 0) {
return;
}
MapCore mapCore = (MapCore)ui->cboxMapCore->itemData(ui->cboxMapCore->currentIndex()).toInt();
int downType = ui->cboxDownType->currentIndex();
QString url = MapDownHelper::getUrl(mapCore, downType, x, y, z);
//打印下载的地址/级别/下载时间(毫秒)
if (error) {
qDebug() << TIMEMS << "图片下载失败" << url << useTime;
//继续重新下载限定最多次数/防止死循环一直下载本来不存在的图片
if (redownCount < 5) {
redownCount++;
redownUrls << url;
mapDown->download(x, y, z);
return;
}
}
//下载成功则将重新下载地址集合移除对应地址
redownCount = 0;
int index = redownUrls.indexOf(url);
if (index >= 0) {
redownUrls.removeAll(url);
qDebug() << TIMEMS << "重新下载成功" << url << useTime;
}
//找到对应级别的进度条更新值
index = z - zoomMin;
QProgressBar *bar = bars.at(index);
int value = bar->value();
int maxValue = bar->maximum();
value = bar->value() + 1;
bar->setValue(value);
QString text = QString("%1/%2 - %3").arg(value).arg(maxValue).arg(z);
ui->txtTip->setText(text);
}
void frmMapDownload::on_btnSelectAll_clicked()
{
foreach (QCheckBox *ck, cks) {
ck->setChecked(true);
}
}
void frmMapDownload::on_btnSelectNone_clicked()
{
foreach (QCheckBox *ck, cks) {
ck->setChecked(false);
}
}
void frmMapDownload::on_btnMapArea_clicked()
{
ui->cboxMapArea->setCurrentIndex(0);
this->runJs("doDraw('rectangle')");
}
void frmMapDownload::on_btnPath_clicked()
{
QString dirName = QFileDialog::getExistingDirectory(this, "选择文件夹");
if (!dirName.isEmpty()) {
ui->txtPath->setText(dirName + "/");
on_cboxDownType_currentIndexChanged(ui->cboxDownType->currentIndex());
}
}
void frmMapDownload::on_btnGet_clicked()
{
this->runJs("getBounds()");
on_cboxDownType_currentIndexChanged(ui->cboxDownType->currentIndex());
}
void frmMapDownload::on_btnClear_clicked()
{
this->runJs("clearOverlay()");
}
bool frmMapDownload::startDownload()
{
//先清空
this->clear();
//判断不同类型做出提示/只提示一次就好
static bool isShow = false;
MapCore mapCore = (MapCore)ui->cboxMapCore->itemData(ui->cboxMapCore->currentIndex()).toInt();
if (!isShow) {
isShow = true;
if (mapCore == MapCore_Tian) {
QtHelper::showMessageBoxInfo("天地图每个秘钥限制了下载张数!", 0, true);
} else if (mapCore == MapCore_Google) {
QtHelper::showMessageBoxInfo("谷歌地图可能需要翻墙才能使用!", 0, true);
}
}
//定义标志位标记是否选中了级别
bool select = false;
int downType = ui->cboxDownType->currentIndex();
QString savePath = ui->txtPath->text().trimmed();
for (int zoom = zoomMin; zoom <= zoomMax; zoom++) {
int index = zoom - zoomMin;
//复位进度条
bars.at(index)->setValue(0);
//判断哪些勾选了
if (cks.at(index)->isChecked() && cks.at(index)->isVisible()) {
select = true;
int xmin = pointLeftBottoms.at(index).x();
int ymin = pointLeftBottoms.at(index).y();
int xmax = pointRightTops.at(index).x();
int ymax = pointRightTops.at(index).y();
//qDebug() << TIMEMS << zoom << index << xmin << ymin << xmax << ymax;
//过滤掉太大的瓦片数/建议分区域下载/不要一次性下载很多瓦片
//一个原因是数量太多没必要/一个原因是后面可能用int会溢出
quint64 count = labs.at(index)->text().toULong();
if (count > 10000000) {
QtHelper::showMessageBoxError("当前级别的瓦片地图数量过多, 请重新选择!");
return false;
}
//如果需要从中断的地方开始下载则修改xmin值即可
//比如之前下载到了101008文件夹/则将xmin改成101008开始就行
//不然一旦中断又需要彻底重新下载该级别的所有文件
//xmin = 101008;
mapDown->download(mapCore, downType, savePath, xmin, xmax, ymin, ymax, zoom);
}
}
if (!select) {
QtHelper::showMessageBoxError("请先勾选要下载的地图级别!");
}
return select;
}
void frmMapDownload::on_btnLoad_clicked()
{
//先清空
on_btnClear_clicked();
//可视区域直接获取边界/行政区域先切换到行政区域然后再获取边界
if (ui->cboxMapArea->currentText() == "可视区域") {
on_btnGet_clicked();
} else {
QString name = ui->cboxCityName->currentText();
QString js = QString("searchBoundary('%1', true)").arg(name);
this->runJs(js);
}
}
void frmMapDownload::on_btnStart_clicked()
{
ui->btnPause->setText("暂停");
if (ui->btnStart->text() == "开始") {
//根据线程数量初始化下载线程队列
mapDown->init(ui->sboxThread->value());
if (this->startDownload()) {
mapDown->start();
ui->btnStart->setText("停止");
ui->widgetPara->setEnabled(false);
ui->btnLoad->setEnabled(false);
ui->btnGet->setEnabled(false);
} else {
mapDown->stop();
}
} else {
mapDown->stop();
//需要延时去清空复位(因为可能停止以后还有一个正在下载的文件在路上返回)
QTimer::singleShot(500, this, SLOT(clear()));
ui->btnStart->setText("开始");
ui->widgetPara->setEnabled(true);
ui->btnLoad->setEnabled(true);
ui->btnGet->setEnabled(true);
}
}
void frmMapDownload::on_btnPause_clicked()
{
//启动阶段才能暂停
if (ui->btnStart->text() == "开始") {
return;
}
mapDown->pause();
if (ui->btnPause->text() == "暂停") {
ui->btnPause->setText("继续");
} else {
ui->btnPause->setText("暂停");
}
}
void frmMapDownload::on_cboxDownType_currentIndexChanged(int index)
{
if (this->isVisible()) {
MapCore mapCore = (MapCore)ui->cboxMapCore->itemData(ui->cboxMapCore->currentIndex()).toInt();
ui->txtPath->setText(MapHelper::getTilePath(mapCore, index));
this->runJs(QString("setMapType(%1)").arg(index));
}
}
void frmMapDownload::on_cboxMapArea_currentIndexChanged(int index)
{
ui->cboxCityName->setEnabled(index == 1);
}
void frmMapDownload::on_tableWidget_cellPressed(int row, int column)
{
//选中行切换复选框
if (row >= 0) {
QCheckBox *cbox = cks.at(row);
cbox->setCheckState(cbox->checkState() == Qt::Checked ? Qt::Unchecked : Qt::Checked);
}
}
四、相关地址
- 国内站点:gitee.com/feiyangqing…
- 国际站点:github.com/feiyangqing…
- 个人作品:blog.csdn.net/feiyangqing…
- 文件地址:pan.baidu.com/s/1ZxG-oyUK… 提取码:o05q 文件名:bin_map.zip
五、功能特点
5.1 地图功能
- 支持多种地图内核,默认采用百度地图,可选高德地图、天地图、腾讯地图、谷歌地图等。
- 同时支持在线地图和离线地图两种模式,离线地图方便在不联网的场景中使用。
- 支持各种地图控件的启用,比如地图导航、地图类型、缩略图、比例尺、全景导航、实时路况、绘图工具、结果面板等。
- 支持多种地图功能的动态启用禁用,比如地图拖曳、键盘操作、滚轮缩放、双击放大、连续缩放、地图测距等。
- 提供众多js函数接口用于交互,参数极其丰富,能够想到的应用场景需求都有。
- 统一的信号槽机制,地图中的结果统一信号发送出去,收到后根据type类型区分。
- 支持地图交互,比如鼠标按下获取对应位置的经纬度。单击标注点弹出对应点的信息。
- 支持添加标注、删除标注、移动标注、清空标注。
- 标注点可以指定图标图片和尺寸,支持gif动图,支持指定以图片中心对齐还是底部中心对齐。可以设置旋转角度,带富文本提示信息。
- 标注点事件支持单击发信号通知和自己弹框显示信息。
- 提供地址转坐标和坐标转地址接口。
- 支持各种图形绘制,包括折线图、多边形、矩形、圆形、弧线等。
- 可显示悬浮的绘图工具栏,直接在地图上划线、标注点、矩形、圆形等。
- 支持各种区域搜索,比如矩形区域、圆形区域,可以按照关键字匹配将搜索结果显示在地图中。
- 可动态添加离线的行政区边界点数据。可以搜索行政区划并获取该区域的边界点数据。数据可以保存到文件以便离线使用。
- 支持点聚合功能,多个小标注点合并到一个大标注点,防止点密集导致交互不友好。
- 可以添加海量点,每个点都可以单击获取对应坐标和信息。
- 所有的覆盖物信息比如标注点、矩形、多边形、折线图等,都可以主动获取对应的信息比如坐标点和路径等。
- 支持路径规划,支持公交路线、自驾路线、步行路线、骑行路线,不同查询支持不同策略,可选最少时间、最少换乘、不走高架等。
- 路径规划结果可以显示在地图中,也可以获取到路径点坐标集合。这个数据可以保存到文件,以便发给机器人或者无人机做导航用来轨迹移动。
- 可以设置不同的地图视图比如街道图、卫星图、混合图。
- 可以设置不同的样式,比如午夜蓝、青草绿等样式风格。
- 可以设置地图的旋转角度和倾斜角度。
- 提供经纬度坐标纠偏转换功能,比如传入的GPS坐标需要转换到百度地图坐标或者高德地图坐标。各种坐标系转换全部离线函数,支持地球坐标系WGS-84、火星坐标系GCJ-02、百度坐标系BD-09之间的互相转换,涵盖了各种地图的坐标系。
- 提供动态轨迹点移动功能,按照给定的经纬度坐标集合平滑移动。
- 同时支持qwidget和qml,支持编译到安卓系统运行。
5.2 其他功能
- 提供离线地图下载模块,可以选择不同的地图内核比如百度地图或者谷歌地图,不同的地图类型比如下载街道图还是卫星图,不同的地图层级,多线程极速下载。
- 表格行实时显示对应的瓦片下载进度,有下载超时时间,重试次数,每个瓦片下载完成都发送信号通知,参数包括下载用时。
- 提供省市轮廓图下载模块,自动下载各个地区的轮廓图,保存到脚本文件或者文本文件。
- 支持手动调整不同区域的轮廓边界,调整后可以主动获取调整后的边界点集合。
- 提供动态点位示例,手动在地图上选点并添加标注,附带自定义的信息比如速度和时间等。
- 提供海量点位示例,批量添加标注点、点聚合、海量点。用于测试环境中支持的最大点位性能。
- 提供动态轨迹示例,在地图上鼠标按下选择起点和终点后,查询路线,获取路径轨迹点,模拟轨迹平滑移动。可以筛选数据将过多的路径点筛选到设定的点数。
- 提供轨迹回放示例,按照指定的轨迹点列表回放,也可以导入轨迹点数据进行回放。同时支持在街道图、卫星图、混合图中回放轨迹。
- 提供省市区域地图示例,采用echart组件,同时支持闪烁点图、迁徙图、区域地图、世界地图、仪表盘等。可以设置标题、提示信息、背景颜色、文字颜色、线条颜色、区域颜色等各种颜色。
- 省市区域地图示例,内置世界地图、全国地图、省份地图、地区地图,可以精确到县,所有地图全部离线使用。可设置城市的名称、值、经纬度集合。
- 内置通用浏览器组件,同时支持webkit/webengine/miniblink等内核。提供网页控件示例,演示打开网页和本地网页文件。
- 支持任意Qt版本、任意系统、任意编译器。