QT+VS调用高德地图的JS API实现地图打点小工具

时间:2024-02-15 16:36:26

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