1. 程式人生 > >【Openlayers】V5.0.2 單點追蹤、實時監控、歷史軌跡、地圖繪製全程式碼例項

【Openlayers】V5.0.2 單點追蹤、實時監控、歷史軌跡、地圖繪製全程式碼例項

1.說明:

  • 該例項是可以用到地圖的常用功能,對付一般和類似的業務場景都綽綽有餘,在圖層的選擇上還是建議選擇天地圖的線上地地圖,選擇4326--84座標系,地圖上沒有偏差。
  • 所有的功能都在一個demo下展示了,就涉及到功能的切換時定時器的關閉和開啟,一般情況下,這些功能都是在單獨的頁面的,所有demo中的定時器的關閉和開啟,有需要的朋友可以看看。
  • 下面的效果只是部分gif展示,因為上傳不了很大GIF動圖。

2.效果展示:

2.1單點追蹤:

zz

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)
            });
        },