QT+VS调用高德地图的JS API实现地图打点小工具
1.QT的WebEngineView模块+VS 的msvc编译器+高德地图的JS API实现,效果如图:
2.程序源码:
QT的pro文件需要加上一行
QT += webengine webenginewidgets webchannel widgets
(1)amapmarktools.html,主要用于显示网页,以及调用高德的JS API进行交互
<!DOCTYPE html> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <meta name="viewport" content="initial-scale=1.0, user-scalable=no" /> <title>高德地图打点小工具</title> <script type="text/javascript" src="https://webapi.amap.com/maps?v=2.0&key=你在高德地图申请的key"></script> <style> *{ margin: 0; padding: 0; } #container { width: 100%; height: 100%; position: absolute; left: 0; top:0; } </style> </head> <body> <div id="container"></div> <script> </script> </body> </html> <script src="qwebchannel.js"></script>//与qt交互需要用到,在本文件同一目录下 <script type="text/javascript"> var map = new AMap.Map("container",{zoom:14,center:[113.324,23.106]}); var MarkIconpath; var amap_bridge; new QWebChannel(qt.webChannelTransport, function (channel) { amap_bridge = channel.objects.amap_bridge; //bridge_name 与QT 中一致 }); var AuthorName=amap_bridge.GetAMapAuthorName(); function getMarkerIconpath(icon_path) { MarkIconpath=icon_path; } function addOneMarker(lat, lng,titleName) { //GPS转高德坐标 var gps = [parseFloat(lng),parseFloat(lat)]; if(titleName=="") { titleName="undefine_markname"; } AMap.convertFrom(gps, 'gps', function (status, result) { if (result.info === 'ok') { var lnglats= result.locations; // Array.<LngLat> var marker = new AMap.Marker( { position:lnglats[0], // 经纬度对象,也可以是经纬度构成的一维数组[116.39, 39.9] title:titleName, icon:MarkIconpath, }); marker.setClickable(true); marker.on("rightclick", function (e){ if (confirm('确定要删除吗') == true) { map.remove(marker); return true; } else { return false; } },"rightclick"); marker.setMap(map); map.setCenter(lnglats[0]); } }); } function removeAllMark() { map.clearMap(); } function saveMarkToFile() { var markeroverlaysList = map.getAllOverlays('marker'); for(var i=0;i<markeroverlaysList.length;i++) { var position=markeroverlaysList[i].getPosition(); var title=markeroverlaysList[i].getTitle(); amap_bridge.saveAllMarkToFileFromAMap(position.lat,position.lng,title); } } </script>
(2)qwebchannel.js,建立qt应用与html的数据交互,本文件由qt官方提供。
地址:https://doc.qt.io/qt-5.9/qtwebengine-webenginewidgets-markdowneditor-resources-qwebchannel-js.html
(3)amapmainwindow.cpp,用于调用WebEngineView,建立与html的数据交互,以及界面显示等.
#pragma execution_character_set("utf-8")//防止中文乱码,并且需要用Notepad++把本文件的编码格式改为ucs-2 Little endian #include "amapmainwindow.h" #include "ui_amapmainwindow.h" #include <QNetworkProxyFactory> #include<QDoubleValidator> #include <QSize> #include<QFileDialog> #include<QTextStream> AMapMainWindow::AMapMainWindow(QWidget *parent) : QMainWindow(parent) , ui(new Ui::AMapMainWindow) { ui->setupUi(this); setWindowTitle("AMapMarkTools地图打点小工具"); p_AmapViewWedget = new QMainWindow();//用于放置webView p_AmapView=new QWebEngineView(p_AmapViewWedget);//将QWebEngineView的父Widget为p_AmapViewWedget p_AmapChannel = new QWebChannel(p_AmapView->page()); //创建通道,与JS交互 p_AmapBridge=new JSWebBridge(p_AmapViewWedget); //创建通道,与JS交互 p_AmapChannel->registerObject("amap_bridge",(QObject*)p_AmapBridge);// 注册名"amap_bridge"与JS中用到的名称保持相同 p_AmapView->page()->setWebChannel(p_AmapChannel);//View与chanel建立连接关系 p_AmapViewWedget->setCentralWidget(p_AmapView); p_AmapHLayoutWidget = new QWidget(this);//用于将webView放置在AMapMainWindow p_AmapHLayoutWidget->setObjectName(QString::fromUtf8("p_AmapHLayoutWidget")); p_AmapHLayoutWidget->setGeometry(QRect(0, 0, 720, 480));//窗口大小为720*480 p_AmapHLayoutWidget->move(150,0); p_AmapHLayout=new QHBoxLayout(p_AmapHLayoutWidget); p_AmapHLayout->addWidget((QWidget *)p_AmapViewWedget); setLayout(p_AmapHLayout); QNetworkProxyFactory::setUseSystemConfiguration(false);//不用代理 QString htmlFilePath =QApplication::applicationDirPath()+"/amapmarktools.html"; loadMarkFilePath=QApplication::applicationDirPath(); p_AmapView->page()->load(QUrl(htmlFilePath)); p_AmapView->page()->setBackgroundColor(Qt::transparent);//背景透明 this->setMinimumWidth(720); this->setMinimumHeight(480); p_operaterLayoutWidget=new QWidget(this); p_addOneMark=new QPushButton(p_operaterLayoutWidget); p_addOneMark->setFixedSize(QSize(100,30)); p_addOneMark->setText("增加标记"); p_addMarkFromFile=new QPushButton(p_operaterLayoutWidget); p_addMarkFromFile->setFixedSize(QSize(100,30)); p_addMarkFromFile->setText("从文件导入标记"); p_removeAllMark=new QPushButton(p_operaterLayoutWidget); p_removeAllMark->setFixedSize(QSize(100,30)); p_removeAllMark->setText("删除所有标记"); p_saveAllMarkToFile=new QPushButton(p_operaterLayoutWidget); p_saveAllMarkToFile->setFixedSize(QSize(100,30)); p_saveAllMarkToFile->setText("导出所有标记"); p_operaterLayout=new QVBoxLayout(p_operaterLayoutWidget); p_operaterLayoutWidget->setGeometry(QRect(0, 0, 200, 480)); p_operaterLayoutWidget->move(0,0); latLineEdit=new QLineEdit(p_operaterLayoutWidget); lngLineEdit=new QLineEdit(p_operaterLayoutWidget); nameLineEdit=new QLineEdit(p_operaterLayoutWidget); latLineEdit->setFixedSize(QSize(100,20)); latLineEdit->setPlaceholderText("gps维度:23.4"); lngLineEdit->setFixedSize(QSize(100,20)); lngLineEdit->setPlaceholderText("gps经度:123.4"); nameLineEdit->setFixedSize(QSize(100,20)); nameLineEdit->setPlaceholderText("标记名称(可选)"); QDoubleValidator *validator = new QDoubleValidator(0, 360, 10, this); lngLineEdit->setValidator(validator); latLineEdit->setValidator(validator); p_operaterLayout->addWidget(latLineEdit); p_operaterLayout->addWidget(lngLineEdit); p_operaterLayout->addWidget(nameLineEdit); p_operaterLayout->addWidget(p_addOneMark); p_operaterLayout->addWidget(p_addMarkFromFile); p_operaterLayout->addWidget(p_saveAllMarkToFile); p_operaterLayout->addWidget(p_removeAllMark); setLayout(p_operaterLayout); p_operaterLayout->setAlignment(Qt::AlignJustify); connect(p_addOneMark, SIGNAL(clicked()),this, SLOT(addOneMarkBtnClickEvent())); connect(p_removeAllMark, SIGNAL(clicked()),this, SLOT(removeAllMarkBtnClickEvent())); connect(p_addMarkFromFile, SIGNAL(clicked()),this, SLOT(addMarkFromFileBtnClickEvent())); connect(p_saveAllMarkToFile, SIGNAL(clicked()),this, SLOT(saveAllMarkToFileBtnClickEvent())); } AMapMainWindow::~AMapMainWindow() { delete ui; } void AMapMainWindow::resizeEvent(QResizeEvent* size) { QSize m_size= this->size(); p_AmapHLayoutWidget->setGeometry(QRect(0, 0, m_size.width()-150,m_size.height()));//窗口大小为720*480 p_AmapHLayoutWidget->move(150,0); p_operaterLayoutWidget->setGeometry(QRect(0, 0, 150, m_size.height())); p_operaterLayoutWidget->move(0,0); } void AMapMainWindow::addOneMark(double lat,double lng,QString labelname) { QString latStr = QString::number(lat, 10, 6); QString lngStr = QString::number(lng, 10, 6); QString jsCmd=QString("addOneMarker('%1','%2','%3')").arg(latStr).arg(lngStr).arg(labelname); qDebug()<<jsCmd; p_AmapView->page()->runJavaScript(jsCmd); } void AMapMainWindow::addOneMarkBtnClickEvent() { qDebug()<<"addOneMarkBtnClickEvent"; if((lngLineEdit->text().length()<1)||(latLineEdit->text().length()<1)) return; double longtitude=lngLineEdit->text().toDouble(); double latitude=latLineEdit->text().toDouble(); if(nameLineEdit->text().length()>=1) { addOneMark(latitude,longtitude,nameLineEdit->text()); } else { addOneMark(latitude,longtitude,""); } getMarkerIconpath((QApplication::applicationDirPath()+"/markerIcon.png")); } void AMapMainWindow::removeAllMarkBtnClickEvent() { removeAllMark(); } void AMapMainWindow::getMarkerIconpath(QString path) { static bool setFlg=false; if(setFlg==false) { QString jsCmd=QString("getMarkerIconpath('%1')").arg(path); qDebug()<<jsCmd; p_AmapView->page()->runJavaScript(jsCmd); setFlg=true; } } void AMapMainWindow::removeAllMark() { QString jsCmd=QString("removeAllMark()"); qDebug()<<jsCmd; p_AmapView->page()->runJavaScript(jsCmd); } void AMapMainWindow::addMarkFromFileBtnClickEvent() { getMarkerIconpath((QApplication::applicationDirPath()+"/markerIcon.png")); QFileDialog fileDialog(this); fileDialog.setWindowTitle(tr("打开文件")); fileDialog.setDirectory(loadMarkFilePath); fileDialog.setNameFilter(tr("*.txt *.log *.*")); fileDialog.setFileMode(QFileDialog::ExistingFiles); fileDialog.setViewMode(QFileDialog::Detail); //fileDialog.setGeometry(200,200,300,300); if(fileDialog.exec() == QDialog::Accepted) { QString path = fileDialog.selectedFiles()[0];//select first filename fileDialog.close(); QFile rfile(path); if(rfile.open(QIODevice::ReadOnly)== true) { QTextStream readTextStream(&rfile); //创建输出流 while(!readTextStream.atEnd()) { double lng=-1; double lat=-1; QString markName; QString oneLineText = readTextStream.readLine(); //读取一行 QString tmpText=oneLineText; int i=0; do { if(i>2) break; if((tmpText.contains(","))) { tmpText=oneLineText.section(',', (i)).trimmed(); qDebug()<<"i="<<i<<",tmpText"<<tmpText; bool isOk=false; switch (i) { case 0: oneLineText.section(',', i,i).trimmed().toDouble(&isOk); if(isOk) { lng=oneLineText.section(',', i,i).trimmed().toDouble(); qDebug()<<"经度:"<<lng; } break; case 1: oneLineText.section(',', i,i).trimmed().toDouble(&isOk); if(isOk) { lat=oneLineText.section(',', i,i).trimmed().toDouble(); qDebug()<<"维度:"<<lat; } break; case 2: markName=oneLineText.section(',', i,i).trimmed(); qDebug()<<"名字:"<<markName; break; default: break; } } i++; }while(tmpText.size()>0); if(lng>0&&lat>0) { qDebug()<<"addOneMark"<<lat<<","<<lng<<","<<markName; addOneMark(lat,lng,markName); } } } } } void AMapMainWindow::saveAllMarkToFile() { QString jsCmd=QString("saveMarkToFile()"); qDebug()<<jsCmd; p_AmapView->page()->runJavaScript(jsCmd); } void AMapMainWindow::saveAllMarkToFileBtnClickEvent() { QString filename = QFileDialog::getSaveFileName(this, tr("选择保存路径"), "", tr("*.txt;; *.log;; *.csv;; *.*")); //选择路径 if(filename.isEmpty()) { return; } else { p_AmapBridge->saveAllMarkPath=filename; qDebug()<<(p_AmapBridge->saveAllMarkPath); } saveAllMarkToFile(); }
(4)jswebbridge.cpp,用于与html建立数据通道
#pragma execution_character_set("utf-8")//防止中文乱码 #include "jswebbridge.h" #include <QMutex> #include <QFile> static QMutex fileMutex; JSWebBridge::JSWebBridge(QObject *parent) : QObject(parent) { saveAllMarkPath="D:/"; } QString JSWebBridge::GetAMapAuthorName() { qDebug()<<"AuthorName:Jest"; return "AuthorName:Jest"; } void JSWebBridge::GetPointLatLngFromAMap(const QString lat,const QString lng,const QString name) { qDebug()<<"来自web标记点输出" <<"维度"<<lat<<","<<"经度"<<lng<<",名字"<<name; } void JSWebBridge::saveAllMarkToFileFromAMap(QString lat,QString lng,QString labelname) { fileMutex.lock(); QFile file(saveAllMarkPath); if(file.open(QIODevice::WriteOnly | QIODevice::Text|QIODevice::Append)) { QString tmp_str=lng+","+lat+","+labelname; QTextStream out(&file); out<<tmp_str<< endl; file.close(); } fileMutex.unlock(); }
3.记录几个踩到的坑
(1)文件编码为utf-8的源文件直接使用到中文会莫名报错,用Notepad++把本文件的编码格式改为ucs-2 Little endian,也可以用[tr("中文")]的方法。
(2)调用高德地图增加mark时,默认图标是只会显示一个很小的坏图片[x],就使用本地路径图片,这路径是通过qt传输的。尝试在AMapMainWindow构造函数传递此路径,但由于建立数据通道初始化未完成,路径没有传输到html保存,还是坏图片。所以在打点前会传输一次路径,路径才在html中保存
(3)在html中要加上这一句,指定调用什么js文件,否则html将无法调用qt中的函数。
<script src="qwebchannel.js"></script>//与qt交互需要用到,在本文件同一目录下
(4)创建通道的步骤也要注意,还有注册名要一致
(5)使用高德地图的JS API时,可以参考官方文档、网上例子,可以在html中用alert函数进行测试数据是否正确
4.导入文本文件的数据格式
格式:经度,维度,mark的title名称(可选)
例如:
113.2223,23.1111,mark1 113.2334,23.5644, 113.8223,23.9111,标记5