You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
			
				
					361 lines
				
				11 KiB
			
		
		
			
		
	
	
					361 lines
				
				11 KiB
			| 
											6 days ago
										 | <!doctype html> | ||
|  | <html lang="zh"> | ||
|  |   <head> | ||
|  |     <meta charset="utf-8" /> | ||
|  |     <meta name="viewport" content="width=device-width, initial-scale=1.0" /> | ||
|  |     <title>轨迹点实现</title> | ||
|  |     <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/ol/ol.css" /> | ||
|  |     <style> | ||
|  |       #map { | ||
|  |         width: 100%; | ||
|  |         height: 500px; | ||
|  |       } | ||
|  |       #controls { | ||
|  |         padding: 10px; | ||
|  |         background: #f5f5f5; | ||
|  |       } | ||
|  |       #info-panel { | ||
|  |         padding: 10px; | ||
|  |         background: white; | ||
|  |         border: 1px solid #ddd; | ||
|  |       } | ||
|  |       .timeline { | ||
|  |         display: flex; | ||
|  |         overflow-x: auto; | ||
|  |         padding: 10px 0; | ||
|  |       } | ||
|  |       .timeline-marker { | ||
|  |         padding: 5px 10px; | ||
|  |         margin: 0 5px; | ||
|  |         background: #1890ff; | ||
|  |         color: white; | ||
|  |         border-radius: 3px; | ||
|  |         cursor: pointer; | ||
|  |       } | ||
|  |     </style> | ||
|  |   </head> | ||
|  |   <body> | ||
|  |     <div id="controls"> | ||
|  |       <button id="btn-play">播放轨迹</button> | ||
|  |       <button id="btn-pause">暂停</button> | ||
|  |       <div id="info-panel">轨迹信息将显示在这里</div> | ||
|  |     </div> | ||
|  |     <div id="map"></div> | ||
|  |     <div class="timeline" id="timeline"></div> | ||
|  |     <script src="https://unpkg.com/ol@9.2.4/dist/ol.js"></script> | ||
|  |     <script> | ||
|  |       // 轨迹数据(示例数据 - 北京到天津的轨迹) | ||
|  |       const trackData = [ | ||
|  |         { time: '09:00', lng: 116.404, lat: 39.915, speed: 60, name: '起点' }, | ||
|  |         { time: '09:15', lng: 116.704, lat: 39.915, speed: 65, name: '大兴区' }, | ||
|  |         { time: '09:30', lng: 116.904, lat: 39.815, speed: 70, name: '廊坊' }, | ||
|  |         { time: '09:45', lng: 117.204, lat: 39.715, speed: 75, name: '武清区' }, | ||
|  |         { time: '10:00', lng: 117.404, lat: 39.615, speed: 80, name: '天津市区' } | ||
|  |       ] | ||
|  | 
 | ||
|  |       // 全局变量 | ||
|  |       let map, trackSource, trackPlayer | ||
|  | 
 | ||
|  |       // 初始化地图 | ||
|  |       function initMap() { | ||
|  |         const source = new ol.source.XYZ({ | ||
|  |           url: 'http://qtbj.icpcdev.site/roadmap/{z}/{x}/{y}.png', | ||
|  |           maxZoom: 16, | ||
|  |           minZoom: 10 | ||
|  |         }) | ||
|  | 
 | ||
|  |         map = new ol.Map({ | ||
|  |           target: 'map', | ||
|  |           layers: [ | ||
|  |             new ol.layer.Tile({ | ||
|  |               source: source | ||
|  |             }) | ||
|  |           ], | ||
|  |           view: new ol.View({ | ||
|  |             center: ol.proj.fromLonLat([116.404, 39.915]), | ||
|  |             zoom: 10 | ||
|  |           }) | ||
|  |         }) | ||
|  | 
 | ||
|  |         // 创建轨迹图层 | ||
|  |         trackSource = new ol.source.Vector() | ||
|  |         const trackLayer = new ol.layer.Vector({ | ||
|  |           source: trackSource, | ||
|  |           style: function (feature) { | ||
|  |             const type = feature.get('type') | ||
|  |             if (type === 'track-line') { | ||
|  |               return new ol.style.Style({ | ||
|  |                 stroke: new ol.style.Stroke({ | ||
|  |                   color: '#1890FF', | ||
|  |                   width: 4, | ||
|  |                   lineDash: [5, 5] | ||
|  |                 }) | ||
|  |               }) | ||
|  |             } else if (type === 'track-point') { | ||
|  |               const speed = feature.get('speed') | ||
|  |               return new ol.style.Style({ | ||
|  |                 image: new ol.style.Circle({ | ||
|  |                   radius: 6, | ||
|  |                   fill: new ol.style.Fill({ | ||
|  |                     color: speed > 70 ? '#FF4D4F' : '#52C41A' | ||
|  |                   }), | ||
|  |                   stroke: new ol.style.Stroke({ | ||
|  |                     color: 'white', | ||
|  |                     width: 2 | ||
|  |                   }) | ||
|  |                 }), | ||
|  |                 text: new ol.style.Text({ | ||
|  |                   text: feature.get('name') || feature.get('time'), | ||
|  |                   offsetY: -15, | ||
|  |                   fill: new ol.style.Fill({ color: '#000' }), | ||
|  |                   stroke: new ol.style.Stroke({ color: '#fff', width: 3 }) | ||
|  |                 }) | ||
|  |               }) | ||
|  |             } else if (type === 'moving-point') { | ||
|  |               return new ol.style.Style({ | ||
|  |                 image: new ol.style.Circle({ | ||
|  |                   radius: 10, | ||
|  |                   fill: new ol.style.Fill({ color: '#FAAD14' }), | ||
|  |                   stroke: new ol.style.Stroke({ | ||
|  |                     color: 'white', | ||
|  |                     width: 3 | ||
|  |                   }) | ||
|  |                 }) | ||
|  |               }) | ||
|  |             } | ||
|  |           } | ||
|  |         }) | ||
|  | 
 | ||
|  |         map.addLayer(trackLayer) | ||
|  |       } | ||
|  | 
 | ||
|  |       // 轨迹播放器类 | ||
|  |       class TrackPlayer { | ||
|  |         constructor(trackData, source, map) { | ||
|  |           this.trackData = trackData | ||
|  |           this.source = source | ||
|  |           this.map = map | ||
|  |           this.currentIndex = 0 | ||
|  |           this.isPlaying = false | ||
|  |           this.animationId = null | ||
|  |           this.movingPoint = null | ||
|  | 
 | ||
|  |           this.initMovingPoint() | ||
|  |           this.createTimeline() | ||
|  |         } | ||
|  | 
 | ||
|  |         // 初始化移动点 | ||
|  |         initMovingPoint() { | ||
|  |           this.movingPoint = new ol.Feature({ | ||
|  |             geometry: new ol.geom.Point( | ||
|  |               ol.proj.fromLonLat([this.trackData[0].lng, this.trackData[0].lat]) | ||
|  |             ), | ||
|  |             type: 'moving-point' | ||
|  |           }) | ||
|  |           this.source.addFeature(this.movingPoint) | ||
|  |         } | ||
|  | 
 | ||
|  |         // 创建时间轴 | ||
|  |         createTimeline() { | ||
|  |           const timeline = document.getElementById('timeline') | ||
|  |           timeline.innerHTML = '' | ||
|  | 
 | ||
|  |           this.trackData.forEach((point, index) => { | ||
|  |             const pointElement = document.createElement('div') | ||
|  |             pointElement.className = 'timeline-point' | ||
|  |             pointElement.innerHTML = ` | ||
|  |         <div>${point.time}</div> | ||
|  |         <div>${point.name}</div> | ||
|  |       ` | ||
|  |             pointElement.addEventListener('click', () => { | ||
|  |               this.jumpToPoint(index) | ||
|  |             }) | ||
|  |             timeline.appendChild(pointElement) | ||
|  |           }) | ||
|  |         } | ||
|  | 
 | ||
|  |         // 播放轨迹 | ||
|  |         play() { | ||
|  |           if (this.isPlaying) return | ||
|  |           this.isPlaying = true | ||
|  |           this.currentIndex = 0 | ||
|  |           this.updateTimeline() | ||
|  |           this.animate() | ||
|  |         } | ||
|  | 
 | ||
|  |         // 暂停播放 | ||
|  |         pause() { | ||
|  |           this.isPlaying = false | ||
|  |           if (this.animationId) { | ||
|  |             cancelAnimationFrame(this.animationId) | ||
|  |           } | ||
|  |         } | ||
|  | 
 | ||
|  |         // 跳转到指定点 | ||
|  |         jumpToPoint(index) { | ||
|  |           this.pause() | ||
|  |           this.currentIndex = Math.max(0, Math.min(index, this.trackData.length - 1)) | ||
|  |           this.updateMovingPoint() | ||
|  |           this.updateTimeline() | ||
|  |         } | ||
|  | 
 | ||
|  |         // 动画循环 | ||
|  |         animate() { | ||
|  |           if (!this.isPlaying || this.currentIndex >= this.trackData.length - 1) { | ||
|  |             this.isPlaying = false | ||
|  |             this.updateInfo('轨迹播放完成') | ||
|  |             return | ||
|  |           } | ||
|  | 
 | ||
|  |           const start = this.trackData[this.currentIndex] | ||
|  |           const end = this.trackData[this.currentIndex + 1] | ||
|  |           this.moveToNextPoint(start, end, 0, 1500) // 1.5秒移动到下一点 | ||
|  |         } | ||
|  | 
 | ||
|  |         // 移动到下一点 | ||
|  |         moveToNextPoint(start, end, progress, duration) { | ||
|  |           if (!this.isPlaying) return | ||
|  | 
 | ||
|  |           if (progress >= 1) { | ||
|  |             this.currentIndex++ | ||
|  |             this.updateTimeline() | ||
|  |             this.animate() | ||
|  |             return | ||
|  |           } | ||
|  | 
 | ||
|  |           // 计算插值坐标 | ||
|  |           const lng = start.lng + (end.lng - start.lng) * progress | ||
|  |           const lat = start.lat + (end.lat - start.lat) * progress | ||
|  | 
 | ||
|  |           // 更新移动点位置 | ||
|  |           this.movingPoint.getGeometry().setCoordinates(ol.proj.fromLonLat([lng, lat])) | ||
|  | 
 | ||
|  |           // 更新信息面板 | ||
|  |           this.updateInfo(end, progress) | ||
|  | 
 | ||
|  |           // 平滑跟随移动点 | ||
|  |           if (progress > 0.3) { | ||
|  |             // 延迟一点开始跟随 | ||
|  |             this.map.getView().animate({ | ||
|  |               center: ol.proj.fromLonLat([lng, lat]), | ||
|  |               duration: 200, | ||
|  |               easing: ol.easing.linear | ||
|  |             }) | ||
|  |           } | ||
|  | 
 | ||
|  |           // 继续动画 | ||
|  |           progress += 16 / duration // 基于60fps | ||
|  |           this.animationId = requestAnimationFrame(() => { | ||
|  |             this.moveToNextPoint(start, end, progress, duration) | ||
|  |           }) | ||
|  |         } | ||
|  | 
 | ||
|  |         // 更新移动点位置(直接跳转) | ||
|  |         updateMovingPoint() { | ||
|  |           const point = this.trackData[this.currentIndex] | ||
|  |           this.movingPoint.getGeometry().setCoordinates(ol.proj.fromLonLat([point.lng, point.lat])) | ||
|  | 
 | ||
|  |           this.map.getView().animate({ | ||
|  |             center: ol.proj.fromLonLat([point.lng, point.lat]), | ||
|  |             zoom: 14, | ||
|  |             duration: 500 | ||
|  |           }) | ||
|  |         } | ||
|  | 
 | ||
|  |         // 更新信息面板 | ||
|  |         updateInfo(point, progress) { | ||
|  |           const progressText = progress ? `进度: ${Math.round(progress * 100)}%` : '' | ||
|  |           document.getElementById('info-panel').innerHTML = ` | ||
|  |       <div><strong>当前位置:</strong> ${point.name}</div> | ||
|  |       <div><strong>时间:</strong> ${point.time}</div> | ||
|  |       <div><strong>速度:</strong> ${point.speed} km/h</div> | ||
|  |       <div><strong>${progressText}</strong></div> | ||
|  |     ` | ||
|  |         } | ||
|  | 
 | ||
|  |         // 更新时间轴高亮 | ||
|  |         updateTimeline() { | ||
|  |           const points = document.querySelectorAll('.timeline-point') | ||
|  |           points.forEach((point, index) => { | ||
|  |             point.classList.toggle('active', index === this.currentIndex) | ||
|  |           }) | ||
|  |         } | ||
|  | 
 | ||
|  |         // 重置轨迹 | ||
|  |         reset() { | ||
|  |           this.pause() | ||
|  |           this.currentIndex = 0 | ||
|  |           this.updateMovingPoint() | ||
|  |           this.updateTimeline() | ||
|  |           this.updateInfo(this.trackData[0], 0) | ||
|  |         } | ||
|  |       } | ||
|  | 
 | ||
|  |       // 添加轨迹到地图 | ||
|  |       function addTrackToMap() { | ||
|  |         // 清空现有轨迹 | ||
|  |         trackSource.clear() | ||
|  | 
 | ||
|  |         // 添加轨迹点 | ||
|  |         trackData.forEach((point, index) => { | ||
|  |           const feature = new ol.Feature({ | ||
|  |             geometry: new ol.geom.Point(ol.proj.fromLonLat([point.lng, point.lat])), | ||
|  |             type: 'track-point', | ||
|  |             time: point.time, | ||
|  |             speed: point.speed, | ||
|  |             name: point.name, | ||
|  |             index: index | ||
|  |           }) | ||
|  |           trackSource.addFeature(feature) | ||
|  |         }) | ||
|  | 
 | ||
|  |         // 添加轨迹线 | ||
|  |         const lineCoords = trackData.map((p) => ol.proj.fromLonLat([p.lng, p.lat])) | ||
|  |         const lineFeature = new ol.Feature({ | ||
|  |           geometry: new ol.geom.LineString(lineCoords), | ||
|  |           type: 'track-line' | ||
|  |         }) | ||
|  |         trackSource.addFeature(lineFeature) | ||
|  | 
 | ||
|  |         // 自适应缩放 | ||
|  |         const extent = trackSource.getExtent() | ||
|  |         map.getView().fit(extent, { | ||
|  |           padding: [50, 50, 50, 50], | ||
|  |           duration: 1000 | ||
|  |         }) | ||
|  | 
 | ||
|  |         // 初始化播放器 | ||
|  |         trackPlayer = new TrackPlayer(trackData, trackSource, map) | ||
|  |       } | ||
|  | 
 | ||
|  |       // 页面加载完成后初始化 | ||
|  |       document.addEventListener('DOMContentLoaded', function () { | ||
|  |         // 初始化地图 | ||
|  |         initMap() | ||
|  | 
 | ||
|  |         // 绑定按钮事件 | ||
|  |         // document.getElementById('btn-add-track').addEventListener('click', addTrackToMap); | ||
|  |         document.getElementById('btn-play').addEventListener('click', function () { | ||
|  |           if (trackPlayer) trackPlayer.play() | ||
|  |         }) | ||
|  | 
 | ||
|  |         // document.getElementById('btn-play') | ||
|  |         // document.getElementById('btn-pause').addEventListener('click', function() { | ||
|  |         //   if (trackPlayer) trackPlayer.pause(); | ||
|  |         // }); | ||
|  |         // document.getElementById('btn-reset').addEventListener('click', function() { | ||
|  |         //   if (trackPlayer) trackPlayer.reset(); | ||
|  |         // }); | ||
|  |         // document.getElementById('btn-clear').addEventListener('click', function() { | ||
|  |         //   trackSource.clear(); | ||
|  |         //   document.getElementById('timeline').innerHTML = ''; | ||
|  |         //   document.getElementById('info-panel').innerHTML = '轨迹已清空'; | ||
|  |         // }); | ||
|  |         // trackPlayer.play(); | ||
|  |         // 页面加载后自动添加示例轨迹 | ||
|  |         setTimeout(addTrackToMap, 1500) | ||
|  |       }) | ||
|  |     </script> | ||
|  |   </body> | ||
|  | </html> |