【Openlayers】V5.0.2 單點追蹤、實時監控、歷史軌跡、地圖繪製全程式碼例項
阿新 • • 發佈:2018-12-30
1.說明:
- 該例項是可以用到地圖的常用功能,對付一般和類似的業務場景都綽綽有餘,在圖層的選擇上還是建議選擇天地圖的線上地地圖,選擇4326--84座標系,地圖上沒有偏差。
- 所有的功能都在一個demo下展示了,就涉及到功能的切換時定時器的關閉和開啟,一般情況下,這些功能都是在單獨的頁面的,所有demo中的定時器的關閉和開啟,有需要的朋友可以看看。
- 下面的效果只是部分gif展示,因為上傳不了很大GIF動圖。
2.效果展示:
2.1單點追蹤:
2.2實時監控:
2.3歷史軌跡:
2.4地圖繪製:
3.程式碼:
3.1要知道:
- 以前一直沒有用過ol,一直覺得ol是個很神奇的東西,以前全部的業務邏輯都是用高德,百度,谷歌。這次藉著公司的前端di地圖改變,學習了下ol,其實ol沒有那麼難。
- 用高德或者百度的說法:我們最常用的功能就是往地圖上打一個marker,OK!在OL這裡全部都是有層的概念,資料容器的概念。名字是我自己起的就是便於理解。加上合理的程式碼抒寫方式,便於我們管理很多層的資料,無論是怎麼樣的業務需求。
- 因為OL用的是大量的面向物件的寫法,很多地方需要大量的物件例項化,因為我們要本著有些例項可以共用,就例項化一次的原則寫程式碼,這樣記憶體的壓力會少很多。我的個人推薦寫法就是提前把共用的例項配置到全域性,用的時候,呼叫下例項就可以。
3.2 追蹤模式:
- 全域性設定一個追蹤的物件體
me.all_obj = { // =======================追蹤 monitor: { // layer: null, // 資料層 data_c: null, // marker p_data: null, // 定時器標識 timer: null, // 重新整理標識 key: true, }, };
- 選擇nav項的單點追蹤,走下面的函式,具體可以看程式碼。
- 初始化函式 1:設定下進入該功能的一些引數設定;2:構建下層物件和資料容器物件。(我自己的習慣)
_monitor: function() { // 初始化引數 me._monitor_set(); // 層構建 me._monitor_layer(); // 打點 me._monitor_p(); // 開始移動 me._monitor_init(); }, // 層資料 _monitor_layer: function() { // 層 me.all_obj.monitor.layer = new ol.layer.Vector(); // 資料容器 me.all_obj.monitor.data_c = new ol.source.Vector(); // 注入層 me.all_obj.monitor.layer.setSource(me.all_obj.monitor.data_c); // 打到地圖上 me.map.addLayer(me.all_obj.monitor.layer); },
- 3:進行資料打點渲染,看下面的程式碼是全部過程。在前端模擬資料的時候,把模擬的走過的路線資料注入我們設定的資料層,就渲染到地圖上了。
// 層資料
_monitor_layer: function() {
// 層
me.all_obj.monitor.layer = new ol.layer.Vector();
// 資料容器
me.all_obj.monitor.data_c = new ol.source.Vector();
// 注入層
me.all_obj.monitor.layer.setSource(me.all_obj.monitor.data_c);
// 打到地圖上
me.map.addLayer(me.all_obj.monitor.layer);
},
// 點
_monitor_p: function() {
// console.log(mk_data_c);
// 建立一個活動圖示需要的Feature,並設定位置
var p_data = new ol.Feature({
// 就一個引數啊,定義座標
geometry: new ol.geom.Point(me.conf.monitor.p)
});
p_data.setStyle(new ol.style.Style({
// 設定一個標識
image: new ol.style.Icon({
src: './img/user.png',
// 這個是相當於是進行切圖了
// size: [50,50],
// 注意這個,竟然是比例 左上[0,0] 左下[0,1] 右下[1,1]
anchor: [0.5, 0.5],
// 這個直接就可以控制大小了
scale: 0.5
}),
text: new ol.style.Text({
// 對其方式
textAlign: 'center',
// 基準線
textBaseline: 'middle',
offsetY: -30,
// 文字樣式
font: 'normal 16px 黑體',
// 文字內容
text: "name:admin",
// 文字填充樣式
fill: new ol.style.Fill({
color: 'rgba(255,255,255,1)'
}),
padding: [5, 5, 5, 5],
backgroundFill: new ol.style.Fill({
color: 'rgba(0,0,255,0.6)'
}),
})
}));
// 資料層收集marker
me.all_obj.monitor.data_c.addFeature(p_data);
// 最優一次
// 最優一次
me._map_fit(me.all_obj.monitor.data_c);
// 拿到全域性
me.all_obj.monitor.p_data = p_data;
},
// 開始追蹤
_monitor_init: function() {
// 追蹤
var old_p = null;
var new_p = [0, 0];
me.all_obj.monitor.timer = setTimeout(function() {
// 得到舊的點
old_p = me.all_obj.monitor.p_data.getGeometry().flatCoordinates;
// ***********************************模擬資料
if (Math.random() > 0.5) {
new_p[0] = old_p[0] + Math.random() * me.conf.monitor.set_num;
} else {
new_p[0] = old_p[0] - Math.random() * me.conf.monitor.set_num;
}
if (Math.random() > 0.5) {
new_p[1] = old_p[1] + Math.random() * me.conf.monitor.set_num;
} else {
new_p[1] = old_p[1] - Math.random() * me.conf.monitor.set_num;
}
// *******************************************
if (me.all_obj.monitor.key) {
// 移動點--改變這個資料就行了
me.all_obj.monitor.p_data.setGeometry(new ol.geom.Point(new_p));
// 線的資料
me._monitor_init_line(new_p, old_p);
//
me._monitor_init();
console.log('monitor');
}
}, me.conf.monitor.time);
},
- 注意這裡在渲染走過的路線的資料的樣式,就是我前面說到的全域性配置好的樣式。me.conf.monitor.line_style
// 初始化線
_monitor_init_line: function(new_p, old_p) {
var line_data = new ol.Feature({
geometry: new ol.geom.LineString([old_p, new_p])
});
line_data.setStyle(me.conf.monitor.line_style);
// 注入容器
me.all_obj.monitor.data_c.addFeature(line_data);
},
- 追蹤功能的全域性配置項:線的樣式,我是提前配置的。
me.conf = {
// 追蹤模式
monitor: {
// 起點座標
p: [116.06, 39.67],
// 波動係數
set_num: 0.05,
// 線的樣式
line_style: new ol.style.Style({
stroke: new ol.style.Stroke({
width: 3,
color: [255, 0, 0, 1],
lineDash: [10, 10],
})
}),
// 重新整理時間
time: 1000,
},
};
3.3 實時監控:
- 實時監控和單點追蹤這個業務場景大同小異。適用於實時ji監控某些點的狀態(例如:報警狀態,實時位置,實時推送的資訊)。該demo的實時監控就是監控了一些點的報警狀態,我這裡簡單的做了兩個狀態,紅色為報警,綠色為正常。
- 這裡沒有優化的地方,也是我目前想不出怎麼優化的地方就是,因為每個點的名字都不一樣,所以我每個點只能例項化一個樣式物件,然後分別注入到資料中。在下次資料過來時,可以找每個點的物件的樣式物件,修改點的狀態的圖片。
- 該次demo沒有嘗試上面的優化方法,有已經嘗試的同學可以留言討論。這裡我用的OL v5.0的版本,比3.0的版本在樣式上存在很多優化,還是建議同學們用最新的API和版本。
- 程式碼實現的風格都是統一的:(前面設定可以配置的引數和監控的物件體,全部監控的函式內部仍然是:初始化引數,構建層和資料容器,開啟資料渲染)
// 實時監控所有
_all_monitor: function() {
// 引數設定
me._all_monitor_set();
// 設定層
me._all_monitor_layer();
// 初始化
me._all_monitor_init(ps_arr);
// 最優一次
me._map_fit(me.all_obj.all_monitor.data_c);
},
3.4 歷史軌跡:
- OL是沒有高德和百度那麼豐富的API的,所以歷史軌跡的還的自己實現。歷史軌跡業務的實現思想就是 定時器加遞迴,在每次到達下一個點的時候,等待一秒移動到下一個點。移動到終點的時候,可以做個友好提示:
// 開始運動
_history_start: function(index) {
// 開始運動
setTimeout(function() {
index++;
if (index == lines_arr.length) {
// 運動完畢
me.conf.history.move_key = false;
layer.msg('運動完畢');
$('#his_s').show();
return;
}
//
else {
console.log('moving---')
me.all_obj.history.p_data.setGeometry(new ol.geom.Point(lines_arr[index]));
me._history_start(index);
}
}, me.conf.history.time);
},
- 歷史軌跡就比較簡單了,點的樣式和線的樣式因為只有一次渲染,可以配置到全域性,也可以在用的時候例項化,個人建議把這些UI性的東西配置到全域性,以防你的產品經理~~~嘿嘿嘿~~
3.5 地圖繪製:
- 地圖繪製其實API不難,難是業務的屢清楚。
- 下面是我自己屢的一些功能節點,不合理的地方請留言。
- 進入地圖繪製功能後,先和後臺請求資料,把原來已經畫好的資料先打在地圖上。現在是進入選擇繪畫模式,這個模式下你可以進行 【選擇樣式】、選擇後繪畫完成後的【繪畫完成】,對當前已經畫的資料【進入編輯】,下面有全部清除資料的【清除畫布】,和對當前層的資料進行【儲存畫布】。
// 圍欄事件的模式選擇
_fence_ev_mode: function() {
// me._fence_sel();
// 有資料--可以清除畫布 可以編輯畫布
if (me.conf.fence.mode == 1) {
$('#tool')
.show()
.html(`
<div class="item mode_2" id="f_edit_ing">開啟編輯</div>
<div class="item mode_2" id="f_edit_done">編輯完成</div>
<div class="item mode_2" id="f_edit_out">退出編輯</div>
`)
.off()
// 開啟編輯
.on('click', '#f_edit_ing', function() {
me._fence_edit_ing();
})
// 編輯完成
.on('click', '#f_edit_done', function() {
me._fence_edit_done();
})
.on('click', '#f_edit_out', function() {
me._fence_edit_out();
});
// 編輯完成先影藏
$('#f_edit_ing').show();
$('#f_edit_done').hide();
}
// 沒有資料--選擇樣式/繪畫完成
else if (me.conf.fence.mode == 2) {
$('#tool')
.show()
.html(`
<div class="item mode_1" id="f_sel">選擇樣式</div>
<div class="item mode_1" id="f_draw_done">繪畫完成</div>
<div class="item mode_1" id="f_in_edit">進入編輯</div>
<div class="item mode_place" >**</div>
<div class="item mode_red" id="f_clear">清除畫布</div>
<div class="item mode_green" id="f_save">儲存畫布</div>
`)
.off()
// 選擇樣式
.on('click', '#f_sel', function() {
me._fence_sel();
})
// 繪製完成
.on('click', '#f_draw_done', function() {
me._fence_draw_done();
})
// 進入編輯
.on('click', '#f_in_edit', function() {
me._fence_in_edit();
})
// 清除
.on('click', '#f_clear', function() {
me._fence_clear();
})
// 儲存資料
.on('click', '#f_save', function() {
me._fence_save();
});
}
},
- 【選擇樣式】進行繪製,我這裡把我img資料夾下面所有的圖片都作為一個樣式可以進行繪製:
- 提供的樣式有 5中,圖示、點、線 、圓、多邊形。需要注意的是,圖片的樣式需要每次例項化,點、線 、圓、多邊形的樣式都可以提前設定。寫部落格的時候發現還可以對圖示的樣式程式碼進行優化,就是每次用圖示的樣式先判斷有沒有,沒有的話就例項化一個,配置到我們的全域性,有的話也是上次例項化的直接用就可以。
- 還需要注意的就是在初始化圖片的時候,就把圖片代表的型別作為屬性付過去,再繪製完成要把這些屬性掛載到資料上,以便在提交的資料的時候,我們知道我們提交的是什麼型別的資料,分別要資料內容的哪些引數。
// ==========================繪畫模式
// 繪製選擇
_fence_sel: function() {
layer.open({
type: 1,
title: false,
area: ['520px', '600px'],
skin: 'cc_layer',
anim: 1,
shade: 0.6,
closeBtn: 0,
btn: false,
content: `
<div class='layer_core' id="layer_core_page">
<div class="title">
選擇樣式
</div>
<div class="box">
<div class="main_box" id="sel_box">
</div>
<div class="tool">
<div class="box">
<span class="cancel" id="cancel">cancel</span>
<span class="save" id="save">save</span>
</div>
</div>
</div>
</div>
`,
success: function(layero, index) {
// 取消事件
me._fence_sel_cancel(index);
// 載入圖片
me._fence_sel_img();
// 繪畫初始化
me._fence_sel_init(index);
},
});
},
// 取消事件
_fence_sel_cancel: function(index) {
// 取消
$('#cancel')
.off()
.on('click', function() {
layer.close(index);
});
},
// 開始繪製的載入圖片
_fence_sel_img: function() {
var str = '';
// 載入圖示
for (var name in me.conf.fence.icon) {
str += `
<div class="normal">
<div class="box" type=${me.conf.fence.icon[name].type} type_id=${name}>
<div class="title">${name}</div>
<img src=${me.conf.fence.icon[name].src} alt="">
</div>
</div>
`;
}
$('#sel_box').html(str);
var key = null;
$('#sel_box')
.off()
.on('click', '.box', function(e) {
key = $(e.currentTarget).hasClass('ac');
// 點選其他專案
if (!key) {
$('#sel_box .box').removeClass('ac');
$(e.currentTarget).addClass('ac');
// 全域性拿到繪製的樣式的ID
me.conf.fence.type = $(e.currentTarget).attr('type');
me.conf.fence.type_id = $(e.currentTarget).attr('type_id');
}
});
},
// 開始繪製初始化
_fence_sel_init: function(index) {
$('#save')
.off()
.on('click', function() {
// 初始化工具
me._fence_sel_tool_drawing();
// 關閉圖層
layer.close(index);
});
},
// 初始化繪畫工具繪畫中
_fence_sel_tool_drawing: function() {
// 清除工具
if (me.all_obj.fence.draw_tool != null) {
me.map.removeInteraction(me.all_obj.fence.draw_tool);
}
// 不是icon
if (me.conf.fence.type != 'icon') {
// 工具
me.all_obj.fence.draw_tool = new ol.interaction.Draw({
type: me.conf.fence.type,
// 注意設定source,這樣繪製好的線,就會新增到這個source裡
source: me.all_obj.fence.data_c,
// 設定繪製時的樣式
style: me.conf.fence.style[me.conf.fence.type],
});
}
// icon
else {
// 設定樣式
me.conf.fence.style[me.conf.fence.type] = new ol.style.Style({
// 繪製的那個標記
image: new ol.style.Icon({
src: me.conf.fence.icon[me.conf.fence.type_id].src,
// 注意這個,竟然是比例 左上[0,0] 左下[0,1] 右下[1,1]
anchor: [0.5, 0.5],
// 這個直接就可以控制大小了
scale: 0.5
}),
});
// 工具
me.all_obj.fence.draw_tool = new ol.interaction.Draw({
type: "Point",
// 注意設定source,這樣繪製好的線,就會新增到這個source裡
source: me.all_obj.fence.data_c,
// 設定繪製時的樣式
style: me.conf.fence.style[me.conf.fence.type],
});
}
// 新增工具
me.map.addInteraction(me.all_obj.fence.draw_tool);
// 每次繪製完成
me.all_obj.fence.draw_tool
.on('drawend', function(event) {
// event.feature 就是當前繪製完成的線的Feature
event.feature.setStyle(me.conf.fence.style[me.conf.fence.type]);
// 掛載屬性
event.feature.type = me.conf.fence.type;
event.feature.type_id = me.conf.fence.type_id;
// console.log(me.conf.fence.type,me.conf.fence.type_id)
});
},