OpenLayers3加载高德离线地图,并在地图上根据经纬度显示标签以及连线、箭头

本文详细介绍如何在OpenLayers3中加载高德离线地图,包括地图下载、服务器虚拟路径配置、OpenLayers3环境搭建及代码实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

0x00

     我并非GIS专业人士,学ol完全是摸着石头过河,之前有一篇博客也是说的加载高德离线地图,但是那个博主不说是用的ol2也就算了,源码也不肯放,然后看他的代码截图是从一个大项目上抠下来的,因此不肯放源码也就可以理解了,但是不肯自己写一个demo作为教程你抠图抠个球,写个几把的教程,呵呵『手动微笑』。

     本文主要面向非GIS专业出身的同学,我尽可能的详细列举操作,并在最后附源码。GIS大牛勿喷,我是一个经不起批评的人,如果你喷我,我就会喷你。

0x01

高德地图下载,此处给出地图下载器的网盘链接:提取码:icrv

我们安装好下载器之后,由于我是示例,故只下载了昆明市的7-18级高德瓦片地图,示例上的经纬度也是昆明市的经纬度,若需要更大范围的地图请自行下载,或者需要操作其他省市的地图请在代码中自行修改。并且代码中经纬度坐标是写死的,实际操作中可以通过接收后台传过来的数据,这个也请自行修改,毕竟是一个demo。

7-18级的昆明瓦片地图大约1.3GB。此处顺便给出昆明市7-18级瓦片网盘链接:提取码:52rl

0x02

下载好之后我的瓦片的路径如下:

下载好瓦片之后因为数据过大,需要在服务器里配置虚拟路径,我用的服务器是Tomcat7。

我们在Tomcat的server.xml文件中做如下修改:

找到<host>标签,在<host>标签中添加如下代码:

<Context debug="0" docBase="E:/km/mapabc/roadmap" path="/maps" reloadable="true"/>

完整的host标签如下:

<Host appBase="webapps" autoDeploy="true" name="localhost" unpackWARs="true">

        <!-- SingleSignOn valve, share authentication between web applications
             Documentation at: /docs/config/valve.html -->
        <!--
        <Valve className="org.apache.catalina.authenticator.SingleSignOn" />
        -->

        <!-- Access log processes all example.
             Documentation at: /docs/config/valve.html
             Note: The pattern used is equivalent to using pattern="common" -->
        <Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs" pattern="%h %l %u %t &quot;%r&quot; %s %b" prefix="localhost_access_log." suffix=".txt"/>
	  <Context debug="0" docBase="此处写瓦片存放路径" path="/maps" reloadable="true"/>
      <Context docBase="此处是你的项目名" path="/此处是你的项目名" reloadable="true" source="org.eclipse.jst.jee.server:12345678"/></Host>

由于我在path里命名为/maps,故配置完成后我们启动tomcat,在浏览器地址栏输入:

http://localhost:8080/maps/7/100/54.png就能访问到瓦片了,效果如下:

配置虚拟路径的教程网上很多,此处就不细说了。另外假如是Eclipse配置虚拟路径不成功的话请自行百度方法解决,这个也很简单。

0x03

配置好虚拟路径之后我们在Eclipse中新建一个项目,在项目中放入OpenLayers3的文件,我用的版本是3.20.1。此处给出网盘链接:提取码:fsn4

js和css都要放在同一个文件夹下,例如我是这样放的:

然后在html中这样引入:

<link rel="stylesheet" type="text/css" href="./ol/ol.css">
	<link rel="stylesheet" type="text/css" href="./css/other.css">
	<script type="text/javascript" src="./ol/ol.js" charset="UTF-8"></script>

此处直接给出other.css文件的样式:

.ol-popup {
            position: absolute;
            background-color: #eeeeee;
            -webkit-filter: drop-shadow(0 1px 4px rgba(0, 0, 0, 0.2));
            filter: drop-shadow(0 1px 4px rgba(0, 0, 0, 0.2));
            padding: 15px;
            border-radius: 10px;
            border: 1px solid #cccccc;
            bottom: 12px;
            left: -50px;
            min-width: 280px;
        }
        .ol-popup:after,
        .ol-popup:before {
            top: 100%;
            border: solid transparent;
            content: " ";
            height: 0;
            width: 0;
            position: absolute;
            pointer-events: none;
        }
        .ol-popup:after {
            border-top-color: #eeeeee;
            border-width: 10px;
            left: 48px;
            margin-left: -10px;
        }
        .ol-popup:before {
            border-top-color: #cccccc;
            border-width: 11px;
            left: 48px;
            margin-left: -11px;
        }
        .ol-popup-closer {
            text-decoration: none;
            position: absolute;
            top: 2px;
            right: 8px;
        }
        .ol-popup-closer:after {
            content: "✖";
        }

此处感谢简书上的大神@你对面的蛇精病,以下是他的文章:使用OpenLayers3来展示一段运动轨迹

0x04

准备工作完成之后直接上代码:

<html>

<head>
	<title>demo</title>
	<link rel="stylesheet" type="text/css" href="./ol/ol.css">
	<link rel="stylesheet" type="text/css" href="./css/other.css">
	<script type="text/javascript" src="./ol/ol.js" charset="UTF-8"></script>
</head>

<body onload="init()">
<div style="width:100%;">
	<div id="map" style="width:100%;"></div>
</div>
	<div id="popup" class="ol-popup">
        <a href="#" id="popup-closer" class="ol-popup-closer"></a>
        <div id="popup-content"></div>
    </div>
	<script type="text/javascript">
		function init(){
		//中心坐标
		var center = ol.proj.transform([102.741501, 25.0527], 'EPSG:4326', 'EPSG:3857');
		
		//设置地图
		var map = new ol.Map({
			view: new ol.View({ 
				//中心位置,设置为起点坐标
				center: center,
				//缩放级别
				zoom: 16,
				minZoom:7,//最小放大级别
			    maxZoom:18//最大放大级别
			}),
			target: 'map'
		});
		
		var offlineMapLayer = new ol.layer.Tile({
			source: new ol.source.XYZ({
				//瓦片的URL,由于只有昆明市地图,在与其他地州交界时会报404
				url: 'http://localhost:8080/maps/{z}/{x}/{y}.png'
			})
		});
		//------------显示弹出层--------------------
		var container = document.getElementById("popup");
    	var content = document.getElementById("popup-content");
    	var popupCloser = document.getElementById("popup-closer");
    	var overlay = new ol.Overlay({
        	element: container,
        	autoPan: true
    	});
    	map.on('click',function(e){
            var pixel = map.getEventPixel(e.originalEvent);
            map.forEachFeatureAtPixel(pixel,function(feature){
                var coordinate = e.coordinate;
                var hdms = ol.coordinate.toStringHDMS(ol.proj.transform(  
			      coordinate, 'EPSG:3857', 'EPSG:4326')); 
                content.innerHTML = "<p>经度:" + feature.H.j+"纬度:"+feature.H.w</p>";
                overlay.setPosition(coordinate);
                map.addOverlay(overlay);
            });
        });
        popupCloser.addEventListener('click',function(){
            overlay.setPosition(undefined);
        });
 		//--------------显示定位标签------------
		var iconFeatures=[];
 		var j = 102.741501;   //起点经度
 		var w = 25.0527;      //起点纬度
		for(var i=0;i<10;i++){//此处循环方式要修改
			j+=0.0001;
			w+=0.001;
			var iconFeature = new ol.Feature({
				  geometry: new ol.geom.Point(ol.proj.transform([j, w], 'EPSG:4326',     
				  'EPSG:3857')),
				  name: 'Null Island',//此处可以随意添加字段以获得你想要的信息
				  starttime:"呵呵1",   //开始时间
				  j:j,                //经度
				  w:w,                //纬度
				  population: 4000,
				  rainfall: 500
				});
				iconFeatures.push(iconFeature);
		}
		var vectorSource = new ol.source.Vector({
			  features: iconFeatures //add an array of features
			});

		var iconStyle = new ol.style.Style({
			image: new ol.style.Icon(({
				anchor: [0.5, 46],
			    anchorXUnits: 'fraction',
			    anchorYUnits: 'pixels',
			    opacity: 0.75,
			    src: 'image/home.png'
			  }))
			});
		var vectorLayer = new ol.layer.Vector({
			  source: vectorSource,
			  style: iconStyle
			});
		//-----------点与点之间绘制箭头--------------------
        map.addLayer(offlineMapLayer);
        map.addLayer(vectorLayer);
        var source = new ol.source.Vector();
      	//实例一个线(标记点)的全局变量
        var geometry = new ol.geom.LineString(); //线,Point 点,Polygon 面
        //散列点数组,放置的点的位置数据
        //传过来的是经纬度,需要坐标转换,此处直接写死转换之后的坐标
        var coordinate = [
                    [11437154.266899377,2882375.478423986],
                    [11437182.930784985,2882523.5751662883],
                    [11437175.764813583,2882628.676080181],
                    [11437204.42869919,2882783.938793885],
            ];
        var points = []     //设置绘制箭头的features
        for(var i=0;i<coordinate.length-1;i++){
            	var dx = coordinate[i+1][0] - coordinate[i][0];
                var dy = coordinate[i+1][1] - coordinate[i][1];
                var rotation = Math.atan2(dy, dx);       //计算箭头方向,即旋转角度
            	var arrow = new ol.Feature({
            		geometry:new ol.geom.Point(coordinate[i])
            	})
            	arrow.setStyle(new ol.style.Style({   //设置此点的样式
     		    	image:new ol.style.Icon({
     		    		src:'image/arrow.png',    //箭头样式
     		    		rotateWithView: false,    //不随view旋转
     		           	rotation: -rotation       //箭头方向,即旋转角度
     		    		})
            	})
            	)
            	points.push(arrow)
        }
        var pointsource = new ol.source.Vector({
            features:points
        });
        var pointlayer = new ol.layer.Vector({
            source: pointsource
     });
        //添加标记点
        function addPonitToGeometry(arr) {
            for (var i = 0; i < arr.length; i++) {
                geometry.appendCoordinate(arr[i]);
            }
        }
        addPonitToGeometry(coordinate);
        var LineStringFeature = new ol.Feature(geometry); //绘制线的数据
        //将线添加到Vector绘制层上
        source.addFeature(LineStringFeature);
        var vectorLayer = new ol.layer.Vector({
            source: source,
            style: new ol.style.Style({
                stroke: new ol.style.Stroke({     //边界样式
                    color: '#f00',    //颜色
                    width: 4          //线段宽度
                })
                })
        });
        map.addLayer(vectorLayer); //将绘制层添加到地图容器中
        map.addLayer(pointlayer);  //将点层添加到地图容器
      //map.removeLayer(vectorLayer);
      }
	</script>
</body>
</html>

画箭头我没有百度到相关的教程,因此我用了一种笨办法:另建一个绘制箭头的图层,根据坐标数组中的起点和终点设置点点样式,将样式设置为箭头。最终的demo效果如下:

最后给出demo的源代码:demo

上面demo的链接用不了了,原因是某些人下载了还给了个差评,不知道是不是没看到我这篇博文,这个只是一个demo,故demo不给了。有需要的私聊我单独发。

html代码都在WebContent目录中,用过eclipse的同学应该都很清楚。

---------------------------end---------------------

<think>我们正在讨论Qt中航线规划开启标注功能的实现方法。根据之前的引用内容,我们可以总结出以下关键点: 1. 引用[1]提到:实现自动加载和保存航线标注点,支持在地图上单击添加标注点,支持删除和移动标注点,标注点带序号图标,绘制航迹线(带方向箭头)。 2. 引用[2]提到:GPS运动轨迹功能需要绘制线条路径,轨迹点信息包括经度、纬度、速度、时间、是否标记等。 3. 引用[3]提到:动态绘制轨迹点的方法,可以使用定时器逐个取出轨迹点坐标,移动标注点(Marker)。 因此,实现航线规划开启标注功能,主要涉及以下几个方面: - 地图交互:处理鼠标事件(单击添加标注点,拖拽移动标注点)。 - 标注点管理:存储标注点的信息(位置、序号等),支持增删改操作,实时更新显示。 - 航迹绘制:根据标注点顺序绘制连线(可带方向箭头),支持删除点后重新绘制。 - 动态轨迹:如果需要模拟运动轨迹,则使用定时器逐步更新标注点位置。 具体步骤: 1. **地图交互设置** - 启用地图的鼠标事件监听,特别是单击事件。当用户单击地图时,获取点击处的经纬度坐标,创建一个标注点(Marker)添加到地图上。同时,将该点添加到标注点列表中。 2. **标注点管理** - 使用一个数据结构(如QList)存储所有标注点。每个标注点可以是一个自定义结构体,包含经纬度、序号、是否标记等信息。 - 实现标注点的删除功能:可以通过右键菜单或者关联的表格来删除选中的标注点。删除后,需要更新剩余标注点的序号,重新绘制航迹。 - 实现标注点的移动:通过监听标注点的拖拽事件,更新其经纬度重新绘制航迹。 3. **航迹绘制** - 使用折线(Polyline)来连接相邻的标注点。为了显示方向,可以在折线上添加箭头(可以通过在折线的每个线段上绘制箭头图标,或者使用带箭头的线样式)。 - 每次标注点增删改时,清除旧的航迹线,根据当前标注点列表重新绘制。 4. **动态轨迹模拟** - 如果需要模拟飞机或船只沿航线的运动,可以创建一个定时器(QTimer)。在定时器的槽函数中,按照预设的轨迹点(可以是航线标注点,也可以是其他轨迹数据)逐步更新一个标注点的位置。 - 轨迹点数据可以预先加载(如从文件读取),也可以实时获取。注意轨迹点可能是多段线,需要分段处理。 5. **与表格联动** - 如引用[1]所述,可以关联一个表格(QTableWidget)来显示所有标注点的信息。当地图上添加标注点时,表格中新增一行;当选中表格行时,地图上对应的标注点高亮;反之,选中地图上的标注点,表格中对应行选中。删除标注点时,从表格中移除对应行。 6. **序列号显示** - 每个标注点可以带有一个序号图标。这可以通过自定义标注点的图标来实现,图标上绘制序号文字。 7. **保存和加载航线** - 将标注点列表(包括经纬度、序号等信息)保存到文件(如XML、JSON或文本文件)。加载时,从文件中读取数据,重新创建标注点绘制航迹。 代码结构示例(伪代码): ```cpp // 自定义标注点结构 struct Waypoint { QPointF coord; // 经纬度 int index; // 序号 // 其他信息... }; class MapWidget : public QWidget { Q_OBJECT public: MapWidget(QWidget *parent = nullptr); // ... 其他成员 private: QList<Waypoint> waypoints; // 存储标注点 QTimer *timer; // 用于动态轨迹 int currentTrajectoryIndex; // 当前轨迹点索引 // 地图相关对象(假设使用QWebEngineView加载地图) QWebEngineView *mapView; private slots: void onMapClicked(const QPointF &coord); // 地图点击事件 void onMarkerDragged(int index, const QPointF &newCoord); // 标注点拖拽事件 void deleteSelectedMarker(); // 删除选中的标注点 void updateTrajectory(); // 更新航迹线 void simulateMovement(); // 定时器槽函数,模拟移动 }; // 实现 void MapWidget::onMapClicked(const QPointF &coord) { // 创建新的标注点 Waypoint wp; wp.coord = coord; wp.index = waypoints.size() + 1; waypoints.append(wp); // 在地图上添加标注点(通过调用JavaScript函数) // 同时设置该标注点可拖拽,连接拖拽完成信号到onMarkerDragged槽 // 更新航迹 updateTrajectory(); // 更新表格 // ... } void MapWidget::onMarkerDragged(int index, const QPointF &newCoord) { // 更新标注点坐标 waypoints[index].coord = newCoord; // 重新绘制航迹 updateTrajectory(); } void MapWidget::deleteSelectedMarker(int index) { // 删除标注点 waypoints.removeAt(index); // 重新排序序号 for (int i = 0; i < waypoints.size(); ++i) { waypoints[i].index = i+1; } // 更新地图上的标注点和航迹 updateTrajectory(); } void MapWidget::updateTrajectory() { // 清除旧航迹 // 调用JavaScript清除地图上的所有航迹线 // 绘制新航迹 if (waypoints.size() < 2) return; // 将waypoints中的点连接成折线 QVector<QPointF> points; for (const Waypoint &wp : waypoints) { points.append(wp.coord); } // 调用JavaScript函数绘制折线(带箭头) // 同时,重新绘制所有标注点(因为序号可能变化) } void MapWidget::simulateMovement() { // 假设我们有一个轨迹点列表(可能是从航线中提取,也可能是另外的轨迹数据) // 轨迹点列表:trajectoryPoints(QList<QPointF>) if (currentTrajectoryIndex >= trajectoryPoints.size()) { timer->stop(); return; } // 获取当前轨迹点 QPointF point = trajectoryPoints[currentTrajectoryIndex++]; // 移动标注点(假设有一个代表飞机/船只的标注点) // 调用JavaScript函数移动该标注点 } ``` 注意:上述代码中,与地图的交互(添加标注点、绘制折线等)通常需要通过调用地图JavaScript API来实现(例如,使用QWebChannel进行通信)。具体实现取决于你使用的地图库(如百度地图高德地图OpenLayers等)。 对于箭头方向,可以在绘制折线时,在每个线段的中点位置添加一个旋转角度计算好的箭头图标(或者使用带箭头的折线样式,如果地图API支持的话)。 以上是一个基本的实现框架,具体细节需要根据所选地图API进行调整。 §§相关问题§§ 1. Qt中如何与JavaScript交互来动态添加和删除地图标注点? 2. 如何实现折线上的方向箭头指示? 3. 在Qt中如何管理大量的标注点,实现高效的增删改查? 4. 如何实现标注点的拖拽功能,实时更新航迹? 5. 如何将标注点数据保存到文件加载?</think>### Qt 航线规划与标注功能实现方法 在 Qt 中实现航线规划与标注功能,需要结合地图组件、事件处理和图形绘制技术。以下是关键实现步骤: #### 1. **地图组件集成** - 使用 `QWebEngineView` 加载 Web 地图(如百度/高德地图 API) - 通过 JavaScript 与地图交互,实现基础地图功能 - 示例代码: ```cpp QWebEngineView *mapView = new QWebEngineView(this); mapView->load(QUrl("qrc:/map.html")); // 加载本地封装的地图HTML ``` #### 2. **标注点管理** - **数据结构**:创建标注点结构体存储坐标和属性 ```cpp struct Waypoint { QPointF coord; // 经纬度坐标 int id; // 唯一标识 QString label; // 标注文本 bool isSelected;// 选中状态 }; QList<Waypoint> waypoints; // 存储所有标注点 ``` - **添加标注**:响应地图点击事件 ```cpp // JavaScript回调到C++ connect(mapView, &QWebEngineView::loadFinished, [=](){ mapView->page()->runJavaScript("initMapClickEvent()"); }); // 接收点击坐标 void MainWindow::addWaypoint(double lat, double lng) { Waypoint wp{ QPointF(lat, lng), waypoints.size()+1 }; waypoints.append(wp); updateMapMarkers(); // 刷新地图标注 } ``` #### 3. **航线绘制** - **折线绘制**:连接相邻标注点形成航线 ```javascript // JavaScript 绘制函数 function drawPolyline(points) { var path = points.map(p => new BMap.Point(p.lng, p.lat)); var polyline = new BMap.Polyline(path, { strokeColor: "#00a1ff", strokeWeight: 3, strokeOpacity: 0.8 }); map.addOverlay(polyline); } ``` - **方向箭头**:在折线顶点添加方向图标[^1] ```cpp // Qt调用JS绘制 void drawDirectionArrow(int startId, int endId) { QString js = QString("addArrow(%1,%2,%3,%4)") .arg(waypoints[startId].coord.x()) .arg(waypoints[startId].coord.y()) .arg(waypoints[endId].coord.x()) .arg(waypoints[endId].coord.y()); mapView->page()->runJavaScript(js); } ``` #### 4. **交互功能实现** - **拖拽标注点**: ```javascript // JS中启用拖拽事件 marker.enableDragging(); marker.addEventListener("dragend", updatePosition); ``` - **删除与重排序**: ```cpp void deleteWaypoint(int id) { waypoints.removeIf([id](const Waypoint& wp){ return wp.id == id; }); renumberWaypoints(); // 重新编号 redrawAllPaths(); // 重绘航线 } ``` #### 5. **数据持久化** - 使用 `QSettings` 或 `QFile` 存储航线数据 ```cpp void saveRouteToFile(const QString& path) { QFile file(path); file.open(QIODevice::WriteOnly); QDataStream out(&file); for (auto& wp : waypoints) { out << wp.coord << wp.id << wp.label; } } ``` #### 6. **动态轨迹模拟**[^3] - 使用 `QTimer` 实现移动效果: ```cpp QTimer *moveTimer = new QTimer(this); connect(moveTimer, &QTimer::timeout, [=](){ if(currentIndex < trajectoryPoints.size()) { moveMarker(trajectoryPoints[currentIndex++]); } }); moveTimer->start(100); // 每100ms移动一次 ``` ### 关键注意事项 1. **坐标转换**:注意经纬度与屏幕像素的转换 2. **性能优化**:大量标注点时使用图层分组管理 3. **异步通信**:Qt 与 JavaScript 的跨线程交互需使用 `QWebChannel` 4. **可视化增强**: - 选中点高亮显示(修改 CSS 样式) - 添加序号标签:`new BMap.Label("1", {position: point})` - 动画效果:使用 `QPropertyAnimation` 实现平滑移动 > 实现效果参考:[自动加载保存航线标注点,支持拖拽和动态轨迹线绘制,带序号和方向箭头指示][^1]
评论 12
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值