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

转载自:https://blog.csdn.net/weixin_42891221/article/details/81703042

You may also like...