一、前言说明

做地图开发会遇到一个常规需求,就是获取当前经纬度对应的海拔高度,也叫做高程值,很遗憾各大地图厂商都未提供接口获取,可能是有明文规定,不能地图中提供对应的海拔高度值,于是需要另想他法,尽管谷歌地图在线的api接口是提供了海拔高度值,但是懂得都懂,国内哪里还能用谷歌地图?完全用不了啊,就算你开发者能用,99.99%的用户也是用不了,而且必须是在线,没有离线也不行。

通过查阅资料得知有个gdal的开源库,支持读取tif文件获取高程值,使用过了是可以,但是编译复杂,尝试过很多次直接集成源码的方式,终归失败,源码数量太多了,两千多个,也有不少的依赖,比如依赖zip和sqlite啥的,所以最终放弃这个方案,后面又找了一些类似tinytiff的开源库,都是用来读取tif文件的,但是没有看到获取高程值接口,而且只支持普通的tif文件。

在折腾的快要放弃的时候,往往就是离成功最近的时候,思索着有没有更简便的方式,而且跨平台,毕竟只是想获取个高程值,引入个这么大的库完全没有必要,有点杀鸡用牛刀的感觉。在经过使用gdal函数接口的过程中,规则都是从图片的像素坐标获取灰度值,然后这个灰度值对应的就是海拔,网上很少有人提到这点,其实这点极其重要,没搞过的人一直云里雾里的折腾编译gdal等开源库。后面还发现对应tif的还有个txt的文本文件,打开内容看到最前面写着左上角的经纬度坐标和水平和垂直比例尺等参数值,然后就是逐行的每个像素点的海拔值,这就非常美妙了,要的就是这种文件呢,自己写个算法去处理也是分分分钟的事情。一气呵成打完收工,自此不仅离线使用,速度纳秒级别,还支持所有平台,就一个类文件100行左右,不要太完美啊。

二、相关代码

#include "frmmain.h"
#include "ui_frmmain.h"
#include "qthelper.h"
#include "gdalobj.h"
#include "demread.h" frmMain::frmMain(QWidget *parent) : QWidget(parent), ui(new Ui::frmMain)
{
ui->setupUi(this);
this->initForm();
//on_btnOpen_clicked();
QMetaObject::invokeMethod(this, "on_btnOpen_clicked", Qt::QueuedConnection);
} frmMain::~frmMain()
{
delete ui;
} bool frmMain::eventFilter(QObject *watched, QEvent *event)
{
if (watched == ui->labImage) {
if (event->type() == QEvent::MouseButtonPress) {
pressed = true;
lastPos = ((QMouseEvent *)event)->globalPos();
} else if (event->type() == QEvent::MouseButtonRelease) {
pressed = false;
} else if (event->type() == QEvent::MouseMove) {
QMouseEvent *mouseEvent = (QMouseEvent *)event;
if (pressed) {
QPoint pos = mouseEvent->globalPos();
QScrollBar *scrollBarx = ui->scrollArea->horizontalScrollBar();
QScrollBar *scrollBary = ui->scrollArea->verticalScrollBar();
int offsetx = pos.x() - lastPos.x();
int offsety = pos.y() - lastPos.y();
scrollBarx->setValue(scrollBarx->value() - offsetx);
scrollBary->setValue(scrollBary->value() - offsety);
lastPos = pos;
} else {
QPoint pos = mouseEvent->pos();
ui->txtPos->setText(QString("%1, %2").arg(pos.x()).arg(pos.y())); int value;
QSize size;
QPointF lnglat;
if (ui->cboxType->currentIndex() == 0) {
if (ui->btnOpen->isEnabled()) {
value = DemRead::getAltitude(pos);
size = QSize(DemRead::width, DemRead::height);
lnglat = DemRead::getLngLat(pos);
}
} else {
value = gdal->getAltitude(pos);
size = gdal->getSize();
lnglat = gdal->getLngLat(pos);
} if (size.width() > 0) {
ui->txtSize->setText(QString("%1 x %2").arg(size.width()).arg(size.height()));
ui->txtLnglat->setText(QString("%1, %2").arg(lnglat.x()).arg(lnglat.y()));
ui->txtAltitude->setText(QString("%1 米").arg(value));
}
}
}
} return QWidget::eventFilter(watched, event);
} void frmMain::initForm()
{
pressed = false;
lastPos = QPoint(); txtFile = QtHelper::appPath() + "/data/dem001.txt";
tifFile = QtHelper::appPath() + "/data/dem001.tif";
gdal = new GdalObj(this); #ifndef Q_CC_MSVC
ui->cboxType->setCurrentIndex(0);
ui->cboxType->setEnabled(false);
#else
ui->cboxType->setCurrentIndex(1);
#endif ui->cboxInput->addItem("89.2883, 42.7083");
ui->cboxInput->addItem("121.4703, 31.2339");
ui->cboxInput->addItem("121.5527, 25.0557");
ui->cboxInput->addItem("121.1792, 26.4213");
ui->cboxInput->setCurrentIndex(0); QPixmap pixmap(tifFile);
ui->labImage->installEventFilter(this);
ui->labImage->setPixmap(pixmap);
ui->labImage->setAttribute(Qt::WA_MouseTracking);
ui->txtSize->setText(QString("%1 x %2").arg(pixmap.width()).arg(pixmap.height()));
} void frmMain::readFinsh()
{
ui->btnOpen->setEnabled(true);
ui->btnGet->setEnabled(true);
ui->txtSize->setText(QString("%1 x %2").arg(DemRead::width).arg(DemRead::height));
} void frmMain::on_btnOpen_clicked()
{
if (ui->cboxType->currentIndex() == 0) {
//这里线程执行/防止卡主界面
ui->btnOpen->setEnabled(false);
ui->btnGet->setEnabled(false);
static QFutureWatcher<void> watcher;
connect(&watcher, SIGNAL(finished()), this, SLOT(readFinsh()));
watcher.setFuture(QtConcurrent::run(DemRead::readFile, txtFile));
} else {
if (gdal->open(tifFile)) {
QSize size = gdal->getSize();
ui->txtSize->setText(QString("%1 x %2").arg(size.width()).arg(size.height()));
}
}
} void frmMain::on_btnGet_clicked()
{
QString text = ui->cboxInput->lineEdit()->text();
QStringList list = text.split(",");
QPointF lnglat(list.at(0).toDouble(), list.at(1).toDouble()); int value;
QPoint pos;
if (ui->cboxType->currentIndex() == 0) {
pos = DemRead::getPos(lnglat);
value = DemRead::getAltitude(lnglat);
} else {
pos = gdal->getPos(lnglat);
value = gdal->getAltitude(lnglat);
} ui->txtLnglat->setText(text);
ui->txtAltitude->setText(QString("%1 米").arg(value));
ui->txtPos->setText(QString("%1, %2").arg(pos.x()).arg(pos.y())); //移动到对应图片中心点
ui->scrollArea->horizontalScrollBar()->setValue(pos.x() - (ui->scrollArea->width() / 2));
ui->scrollArea->verticalScrollBar()->setValue(pos.y() - (ui->scrollArea->height() / 2));
}

三、相关地址

  1. 文件地址:https://pan.baidu.com/s/1ZxG-oyUKe286LPMPxOrO2A 提取码:o05q 文件名:bin_map.zip
  2. 国内站点:https://gitee.com/feiyangqingyun
  3. 国际站点:https://github.com/feiyangqingyun
  4. 在线文档:http://www.qtcdev.com/map/

四、效果图

五、功能特点

5.1 地图功能

  1. 支持多种地图内核,默认采用百度地图,可选高德地图、天地图、腾讯地图、谷歌地图等。
  2. 同时支持在线地图和离线地图两种模式,离线地图方便在不联网的场景中使用。
  3. 支持各种地图控件的启用,比如地图导航、地图类型、缩略图、比例尺、全景导航、实时路况、绘图工具、结果面板等。
  4. 支持多种地图功能的动态启用禁用,比如地图拖曳、键盘操作、滚轮缩放、双击放大、连续缩放、地图测距等。
  5. 提供众多js函数接口用于交互,参数极其丰富,能够想到的应用场景需求都有。
  6. 统一的信号槽机制,地图中的结果统一信号发送出去,收到后根据type类型区分。
  7. 支持地图交互,比如鼠标按下获取对应位置的经纬度。单击标注点弹出对应点的信息。
  8. 支持添加标注、删除标注、移动标注、清空标注。
  9. 标注点可以指定图标图片和尺寸,支持gif动图,支持指定以图片中心对齐还是底部中心对齐。可以设置旋转角度,带富文本提示信息。
  10. 标注点事件支持单击发信号通知和自己弹框显示信息。
  11. 提供地址转坐标和坐标转地址接口。
  12. 支持各种图形绘制,包括折线图、多边形、矩形、圆形、弧线等。
  13. 可显示悬浮的绘图工具栏,直接在地图上划线、标注点、矩形、圆形等。
  14. 支持各种区域搜索,比如矩形区域、圆形区域,可以按照关键字匹配将搜索结果显示在地图中。
  15. 可动态添加离线的行政区边界点数据。可以搜索行政区划并获取该区域的边界点数据。数据可以保存到文件以便离线使用。
  16. 支持点聚合功能,多个小标注点合并到一个大标注点,防止点密集导致交互不友好。
  17. 可以添加海量点,每个点都可以单击获取对应坐标和信息。
  18. 所有的覆盖物信息比如标注点、矩形、多边形、折线图等,都可以主动获取对应的信息比如坐标点和路径等。
  19. 支持路径规划,支持公交路线、自驾路线、步行路线、骑行路线,不同查询支持不同策略,可选最少时间、最少换乘、不走高架等。
  20. 路径规划结果可以显示在地图中,也可以获取到路径点坐标集合。这个数据可以保存到文件,以便发给机器人或者无人机做导航用来轨迹移动。
  21. 可以设置不同的地图视图比如街道图、卫星图、混合图。
  22. 可以设置不同的样式,比如午夜蓝、青草绿等样式风格。
  23. 可以设置地图的旋转角度和倾斜角度。
  24. 提供经纬度坐标纠偏转换功能,比如传入的GPS坐标需要转换到百度地图坐标或者高德地图坐标。各种坐标系转换全部离线函数,支持地球坐标系WGS-84、火星坐标系GCJ-02、百度坐标系BD-09之间的互相转换,涵盖了各种地图的坐标系。
  25. 提供动态轨迹点移动功能,按照给定的经纬度坐标集合平滑移动。
  26. 同时支持qwidget和qml,支持编译到安卓系统运行。

5.2 其他功能

  1. 提供离线地图下载模块,可以选择不同的地图内核比如百度地图或者谷歌地图,不同的地图类型比如下载街道图还是卫星图,不同的地图层级,多线程极速下载。
  2. 表格行实时显示对应的瓦片下载进度,有下载超时时间,重试次数,每个瓦片下载完成都发送信号通知,参数包括下载用时。
  3. 提供省市轮廓图下载模块,自动下载各个地区的轮廓图,保存到脚本文件或者文本文件。
  4. 支持手动调整不同区域的轮廓边界,调整后可以主动获取调整后的边界点集合。
  5. 提供动态点位示例,手动在地图上选点并添加标注,附带自定义的信息比如速度和时间等。
  6. 提供海量点位示例,批量添加标注点、点聚合、海量点。用于测试环境中支持的最大点位性能。
  7. 提供动态轨迹示例,在地图上鼠标按下选择起点和终点后,查询路线,获取路径轨迹点,模拟轨迹平滑移动。可以筛选数据将过多的路径点筛选到设定的点数。
  8. 提供轨迹回放示例,按照指定的轨迹点列表回放,也可以导入轨迹点数据进行回放。同时支持在街道图、卫星图、混合图中回放轨迹。
  9. 提供省市区域地图示例,采用echart组件,同时支持闪烁点图、迁徙图、区域地图、世界地图、仪表盘等。可以设置标题、提示信息、背景颜色、文字颜色、线条颜色、区域颜色等各种颜色。
  10. 省市区域地图示例,内置世界地图、全国地图、省份地图、地区地图,可以精确到县,所有地图全部离线使用。可设置城市的名称、值、经纬度集合。
  11. 内置通用浏览器组件,同时支持webkit/webengine/miniblink等内核。提供网页控件示例,演示打开网页和本地网页文件。
  12. 支持任意Qt版本、任意系统、任意编译器。

Qt/C++离线读取全国任意经纬度高程海拔值/无任何依赖/纯原创代码解析的更多相关文章

  1. JDBC读取新插入Oracle数据库Sequence值的5种方法

    Oracle的sequence实现非常灵活,所以也带来一些易用性问题,如何取到新插入记录生成的sequence值与其它数据库有较大差别,本文详国介绍了5种实现读取新插入记录sequence值的方法. ...

  2. Spring中使用@Value读取porperties文件中的属性值方法总结及注意事项

    本文为博主原创,转载请注明出处. 此前曾总结过使用工具类读取properties文件中的属性值,有兴趣的可以看一下. 如何快速获取properties中的配置属性值:https://www.cnblo ...

  3. 7 -- Spring的基本用法 -- 10... 获取其他Bean的属性值;获取Field值;获取任意方法的返回值

    7.10 高级依赖关系配置 组件与组件之间的耦合,采用依赖注入管理:但基本类型的成员变量值,应直接在代码中设置. Spring支持将任意方法的返回值.类或对象的Field值.其他Bean的getter ...

  4. AtomicLong可以被原子地读取和写入的底层long值的操作

    java.util.concurrent.atomic.AtomicLong类提供了可以被原子地读取和写入的底层long值的操作,并且还包含高级原子操作. AtomicLong支持基础long类型变量 ...

  5. java使用poi读取doc和docx文件(maven自动导入依赖包)

    java使用poi读取doc和docx文件(maven自动导入依赖包) 于是在网上搜寻了一阵之后才发现原来doc文档和excel一样不能用普通的io流的方法来读取,而是也需要用poi,于是进行了一番尝 ...

  6. PHP根据传入的经纬度,和距离范围,返回所有在距离范围内的经纬度的取值范围

    /** * 根据传入的经纬度,和距离范围,返回所有在距离范围内的经纬度的取值范围 * @param float $lng 经度 * @param float $lat 纬度 * @param floa ...

  7. Qt开发技术:QCharts(三)QCharts样条曲线图介绍、Demo以及代码详解

    若该文为原创文章,未经允许不得转载原博主博客地址:https://blog.csdn.net/qq21497936原博主博客导航:https://blog.csdn.net/qq21497936/ar ...

  8. js读取cookie 根据cookie名称获取值的方法

    //方法1 //存在问题:如果cookie中存在 aaaname=aa;name=bb 获取name的值就会出现错误function getCookie(c_name){ if (document.c ...

  9. Qt通过odbc读取excel数据

    传统的读取方式是通过Excel.Application,这种方式不仅操作繁琐,而且速度也不快. 通过odbc读取,可以使用select语句直接读取整个工作表,处理excel数据就跟数据库一样方便. 当 ...

  10. 【QT】二进制读取图像文件并显示

    打开对话框选择文件 二进制方式读取文件 转换成图像显示 void MainWindow::showImage() { //打开文件对话框 QString lastPath="D:/Engli ...

随机推荐

  1. dotnet 委托delegate的使用 定义和使用

    void Main() { // 委托 - 初级和高级的分水岭 // 1. 委托的初体验 // 委托是一个引用类型,其实是一个类型,保存方法的指针(地址) (变量名字都是地址 都是指针) // 是一个 ...

  2. element-admin - 图片上传组件 ImageUpload

    预览详情 : 父组件:注册引入子组件 :只需要传递一个数据  limit  :这是限制显示多少张图片 : 子组件: - 图片上传 - el-upload 代码 : <template> & ...

  3. 使用doccano标注NER数据详细教程

    使用doccano标注NER数据详细教程 说明: 首次发表日期:2024-10-12 参考资料: https://github.com/zjunlp/DeepKE/blob/main/README_T ...

  4. MIL-STD-1553B总线通信模块(1553B板卡)

    MIL-STD-1553B总线通信模块(1553B板卡)产品具有以下特点: 1.产品覆盖多种接口CPCI/PXI/PCI/PC104/PC104+/USB等,满足用户不同平台的使用要求: 2.自主知识 ...

  5. RocketMQ 5.0 如何配置TLS加密传输?

    本文作者:李伟,社区里大家叫小伟,Apache RocketMQ Committer,RocketMQ Python客户端项目Owner ,Apache Doris Contributor,腾讯云Ro ...

  6. i-MES生产制造管理系统-可视化看板

    可视化看板最主要的目的是为了将生产状况透明化,让大家能够快速了解当前的生产状况以及进度,通过大数据汇总分析,为管理层做决策提供数据支撑,看板数据必须达到以下基本要求: 数据准确--真实反映生产情况 数 ...

  7. ext 库及 pb_ds 在 OI 中的应用

    ext 库在 OI 中的应用 写一个帖子,防止以后忘了. pb_ds 部分 pb_ds 万能头 #include<bits/extc++.h> 来包含 ext 库中所有的头文件(例如 pb ...

  8. pytorch的四个hook函数

    训练神经网络模型有时需要观察模型内部模块的输入输出,或是期望在不修改原始模块结构的情况下调整中间模块的输出,pytorch可以用hook回调函数来实现这一功能.主要使用四个hook注册函数:regis ...

  9. vue遇到Conflicting order. Following module has been added:(加载顺序冲突)

    其中article.vue和topGroup.vue这两个文件在模块unitTest和wrongBook上出现加载冲突 其中一个文件先加载topGroup.vue文件其中一个文件先加载article. ...

  10. mysql - 修改字段名称 & 修改字段数据类型

    修改字段名称: ALTER TABLE <表名> CHANGE <旧字段名> <新字段名> <新数据类型>: mysql> ALTER TABLE ...