1. 程式人生 > 程式設計 >Openlayers3實現車輛軌跡回放功能

Openlayers3實現車輛軌跡回放功能

記錄基於geoserver地圖服務,Openlayers3在web前端實現車輛軌跡回放功能,並記錄和解決過程中出現的linestring只描繪部分經緯度座標問題。

參考Openlayers3 官網例子

Openlayers3實現車輛軌跡回放功能

html

<!DOCTYPE html>
<html lang="en">
<head>
 <title>車輛軌跡回放</title>
 <meta charset="UTF-8"/>
 <meta name="viewport" content="width=device-width,initial-scale=1.0"/>
 <link rel="stylesheet" href="../css/bootstrap.min.css"/>
 <link rel="stylesheet" href="../ol/ol.css"/>
 <style>
 #map {
  position: relative;
 }
 .popover{
  min-width: 60px;
 }
 html{height:100%}
 body{height:100%;margin:0px;padding:0px}
 </style>
</head>
<body style="margin: 0px 0px 0px 0px;line-height:0px;">
<div id="content">
 <!--<div id="map" style="height: 100%;width:100%"></div>-->
 <div class="row-fluid">
 <div>
  <div id="map" class="map"></div>
 </div>
 </div>

 <div class="row-fluid">
 <div class="span3" style="position:absolute;top:0px;right:0px;">
  <div class="accordion-group widget-box">
  <div class="accordion-heading">
   <div class="widget-title"><a data-parent="#collapse-group" href="#collapseGOne"
       data-toggle="collapse"><span class="icon"><i
    class="icon-map-marker"></i></span>
   <h5>軌跡查詢</h5>
   </a>
   </div>
  </div>
  <div class="accordion-body in collapse" id="collapseGOne">
   <div class="form-actions">
   <div class="control-group" style="margin-bottom: 0px">
    <label class="control-label"><i class="icon-truck"></i>裝置</label>

    <div class="controls">
    <select id="busSelect" class="span10">
     <option value="*">請選擇裝置</option>
    </select>
    </div>
   </div>
   </div>


   <div class="form-actions">
   <div class="control-group" style="margin-bottom: 0px">
    <label class="control-label"><i class="icon-table"></i>日期</label>

    <div class="controls">
    <div data-date="" class="input-append date datepicker">
     <input id="traceday" type="text" data-date-format="yyyy-mm-dd" class="span10"
      disabled>
     <span class="add-on"><i class="icon-time"></i></span></div>
    </div>
   </div>
   </div>
   <div style="padding: 19px 20px 20px; margin-top: 20px; margin-bottom: 20px;">
   <div class="control-group" style="margin-bottom: 0px">
   <button id="queryBtn" class="btn btn-primary"><i class="icon-search"></i>&nbsp;軌跡查詢</button> 
   <span class="remind"></span>
  </div>
   <div class="control-group" style="margin-top: 10px">
   <button id="animateBtn" class="btn btn-info"><i class="icon-eye-open"></i>&nbsp;軌跡回放</button> 
   <input id="speed" type="range" min="1" max="100" step="10" value="10">&nbsp;<span><i class="icon-cog">速度</i></span>
  </div>
  </div>
 </div>
 </div>
</div>
</div>
</div>
</div>
<script src="../js/lib/jquery.min.js"></script>
<script src="../js/lib/bootstrap.min.js"></script>
<script src="../ol/ol-debug.js"></script>
<script src="../ol/ol.js"></script>
<script src="../js/globalVariable.js"></script>
<script src="../js/gpsQueryAndroid.js"></script>
</body>
</html>

map初始化

$(document).ready(function () {
 map = new ol.Map({
 logo: false,target: document.getElementById('map'),layers: layers,view: view
 });
 initSelect();//裝置下拉框列表初始化
 //時間控制元件初始化
 $('#s-traceday').val(transfromTime(new Date(),false)+" 00:00:00");
 $('#e-traceday').val(transfromTime(new Date(),true));
 $('.datepicker').datetimepicker({
 minView:0,format: 'yyyy-MM-dd hh:mm:ss',todayBtn : "linked",autoclose : true,todayHighlight : true,startDate: daylimit,endDate:'+1d'//結束時間,在這時間之後都不可選
 });
});

//軌跡line layer
var vsource = new ol.source.Vector({
 type: 'LineString',features: []
});
var linelayers = new ol.layer.Vector({
 source: vsource,style: new ol.style.Style({
 fill: new ol.style.Fill({
  color: '#0044CC'
 }),stroke: new ol.style.Stroke({
  color: '#0044CC',width: 4
 })
 })
});
//地圖基礎引數
var map;
var center = [121.6606763113213,31.14611063632111];
var lng,lat;
var source = new ol.source.Vector({
 wrapX: false
});;
var projection = new ol.proj.Projection({
 code: 'EPSG:4326',units: 'degrees',axisOrientation: 'neu'
});
var view = new ol.View({
 projection: projection,center: center,zoom: 16
});

var layers = [new ol.layer.Tile({
 title: '、地圖',visible: true,preload: Infinity,source: new ol.source.TileWMS({
  url: gisUrl,params: {
  'VERSION': '1.1.0',tiled: true,STYLES: '',LAYERS: 'shanghai:maptest',}
 })
 }),linelayers];

ajax獲取座標資料

//軌跡查詢按鈕點選
var positions=[];
$("#queryBtn").click(function(){
 //清除之前的圖層
 if (centerSource.getFeatures != null) {
  centerSource.clear(); }
 linelayers.getSource().clear(true);
 positions=[];//清空
 //取值
 var busnum=$("#busSelect").val();
 var traceday=$("#traceday").val();
 if(busnum=="*"){
 $(".remind").html('<i class="icon-info-sign">請先選擇車輛</i>');
 return;
 }else{
 $(".remind").html('');
 busnum=busnum.slice(2);
 }
 if(transfromTime(new Date(),false)==traceday){//當天
 traceday="*";
 }else{
 traceday=traceday.replace(/(-)/g,"");
 }
 $(".remind").html('<i class="icon-cogs"> 正在查詢...</i>');

 //請求
 $.getJSON(baseUrl+"trace/query/"+busnum+"/"+traceday,"",function(data){
  if(data.length==0){
  $(".remind").html('<i class="icon-info-sign">未查到gps資料</i>');return;
  }
 var position = [];
 for(var i = 0;i<data.length;i++){
  if(i!=0&&data[i].lon==data[i-1].lon&&data[i].lon==data[i-1].lon){//去除重複資料
   continue;
  }
  position = [parseFloat(data[i].lon),parseFloat(data[i].lat)];
  positions.push(position);
 }
 console.log(positions);
 if(positions.length==1){
  $(".remind").html('<i class="icon-info-sign">該車輛當天停在該位置未啟動</i>');
  centerAt(positions[0]);
  return;
 }
 AddLayer(positions);
 });
});

顯示軌跡

//軌跡描繪
function AddLayer() {
 var lineFeature = new ol.Feature({//路線
 geometry: new ol.geom.LineString(positions,'XY'),});
 linelayers.getSource().addFeature(lineFeature);

 var startFeature = new ol.Feature({//起點
 geometry: new ol.geom.Point(positions[0]),population: 4000,rainfall: 500
 });
 startFeature.setStyle(startStyle);
 linelayers.getSource().addFeature(startFeature);
 var endFeature = new ol.Feature({//終點
 geometry: new ol.geom.Point(positions[positions.length-1]),rainfall: 500
 });
 endFeature.setStyle(endStyle);
 linelayers.getSource().addFeature(endFeature);


 carFeature = new ol.Feature({//車子
 geometry: new ol.geom.Point(positions[0]),rainfall: 500
 });
 carFeature.setStyle(carStyle);
 linelayers.getSource().addFeature(carFeature);
 var extent = linelayers.getSource().getExtent();//合適比例縮放居中
 view.fit(extent,map.getSize());
 }

顯示單點

//居中 車輛不運動時居中顯示圖示處理
var centerLayer = null;
var centerSource = new ol.source.Vector({
 features: null
});
//居中在一個位置
function centerAt(position) {
 var pan = ol.animation.pan({
 duration: 2000,source: (view.getCenter())
 });
 view.setCenter(position);

 var iconFeature = new ol.Feature({
 geometry: new ol.geom.Point(position),name: 'Null Island',rainfall: 500
 });
 iconFeature.setStyle(iconStyle);
 centerSource.addFeature(iconFeature);
 centerLayer = new ol.layer.Vector({
 source: centerSource
 });
 map.addLayer(centerLayer);
 centerLayer.setVisible(true);
}

點在線上運動,軌跡回放

//軌跡回放start 參考官網
var carFeature = null;
var speed,now;
var animating = false;
$("#animateBtn").click(function(){
 if(positions.length==0){
  $(".remind").html('<i class="icon-info-sign">請先查詢軌跡</i>');
  return;
 }
 if (animating) {
  stopAnimation(false);
  } else {
  animating = true;
  now = new Date().getTime();
  speed = $("#speed").val();//速度
  $("#animateBtn").html('<i class="icon-eye-close"></i>&nbsp;取消回放');
  carFeature.setStyle(null);
  // map.getView().setCenter(center);
  map.on('postcompose',moveFeature);
  map.render();
  }
});

var moveFeature = function(event) {
 var vectorContext = event.vectorContext;
 var frameState = event.frameState;

 if (animating) {
 var elapsedTime = frameState.time - now;
 // here the trick to increase speed is to jump some indexes
 // on lineString coordinates
 var index = Math.round(speed * elapsedTime / 1000);

 if (index >= positions.length) {
  stopAnimation(true);
  return;
 }

 var currentPoint = new ol.geom.Point(positions[index]);
 var feature = new ol.Feature(currentPoint);
 vectorContext.drawFeature(feature,carStyle);
 }
 // tell OL3 to continue the postcompose animation
 map.render();
 };

 function startAnimation() {
 if (animating) {
 stopAnimation(false);
 } else {
 animating = true;
 now = new Date().getTime();
 speed = speedInput.value;
 $("#animateBtn").html('<i class="icon-eye-close"></i>&nbsp;取消回放');
 // hide geoMarker
 geoMarker.setStyle(null);
 // just in case you pan somewhere else
 map.getView().setCenter(center);
 map.on('postcompose',moveFeature);
 map.render();
 }
 }

 function stopAnimation(ended) {
  animating = false;
  $("#animateBtn").html('<i class="icon-eye-open"></i>&nbsp;軌跡回放');

  // if animation cancelled set the marker at the beginning
  var coord = ended ? positions[positions.length - 1] : positions[0];
  /** @type {ol.geom.Point} */ (carFeature.getGeometry())
  .setCoordinates(coord);
  //remove listener
  map.un('postcompose',moveFeature);
 }
//軌跡回放end

解決linestring座標顯示不全

期間碰到一個問題

var lineFeature = new ol.Feature({//路線
 geometry: new ol.geom.LineString(positions,});
 linelayers.getSource().addFeature(lineFeature);

呼叫這段程式碼顯示軌跡時,從資料庫取到20個座標,就可能只顯示4個座標,本來是彎曲的軌跡,但是實際上就是折線,很尷尬。
誤打誤撞,和同學 交流過程中發現問題所在,特此感謝。
在ajax獲取座標資料中發現問題所在:
原先錯誤程式碼

position = [data[i].lon,data[i].lat];
  positions.push(position);

正確程式碼

position = [parseFloat(data[i].lon),parseFloat(data[i].lat)];
  positions.push(position);

原因就是沒有把float型別的座標利用parseFloat強轉,導致預設的泛資料型別精確度不夠,經緯度小數點後末尾幾位就會被忽略,於是造成資料失效,描出的線就會有問題。

附上icon、起點、終點、車輛等地圖樣式

//樣式,供上述程式碼呼叫
var iconStyle = new ol.style.Style({
 image: new ol.style.Icon(/** @type {olx.style.IconOptions} */ ({
 anchor: [0.5,0.8],anchorXUnits: 'fraction',anchorYUnits: 'pixels',opacity: 0.75,src: 'img/iconfont-weizhi-red.png'
 }))
});
var startStyle = new ol.style.Style({
 image: new ol.style.Icon(/** @type {olx.style.IconOptions} */ ({
  anchor: [0.5,opacity: 0.8,src: 'img/start.png'
 /*anchorXUnits: 'fraction',*/

 }))
});
var endStyle = new ol.style.Style({
 image: new ol.style.Icon(/** @type {olx.style.IconOptions} */ ({
 src: 'img/end.png',anchor: [0.5,}))
});
var carStyle = new ol.style.Style({
 image: new ol.style.Icon(/** @type {olx.style.IconOptions} */ ({
 src: 'img/car.png',}))
});

以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支援我們。