前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >ol4通过ImageCanvas实现大量点的展示以及交互的实现

ol4通过ImageCanvas实现大量点的展示以及交互的实现

作者头像
lzugis
发布2019-08-29 11:26:53
6950
发布2019-08-29 11:26:53
举报

概述

在ol4里面可以通过Vector Layer的方式进行点的渲染,但是当点的个数比较多的时候,会存在明显的操作不流畅。本文讲述如何利用ImageCanvas接口,对大量的点进行展示,并添加相应的交互。

实现效果

实现效果
实现效果

实现分析

1.效率差异如何得来?

ol的最终渲染也是通过canvas的方式来渲染的,但是为什么效率会差这么多呢?分析原因,是因为: 1)Vector的渲染方式会保留很多交互相关的操作; 2)ImageCanvas的方式其实是将数据渲染到一个新的画布上,再以图片的方式渲染到map的canvas上;

2.交互如何实现

地图上的渲染查看相关的接口实现起来比较简单,这里重点说一下渲染完后如何交互。在实现地图交互的时候,存在两个技术点: 1)如何判断鼠标经过的位置要触发交互的位置? 我们知道每一个点的大小是指的像素值,所以在判断位置的时候调用view的getResolution()接口,实时计算一个半径,通过鼠标当前点+半径可以创建一个圆,在判断落在圆内的点就为交互的点。代码实现如下:

代码语言:javascript
复制
map.on('pointermove', function (e) {
  var coord = e.coordinate;
  var res = map.getView().getResolution();
  var _radius = res * r;
  var _circle = new ol.geom.Circle(coord, _radius);
  var lonlat = ol.proj.toLonLat(coord);
  var index = getLonLatIndex(lonlat[0], lonlat[1]);
  if(index !== -1) {
    var data = dataCache[index].data;
    for (var i = 0; i < data.length; i++) {
      var d = data[i];
      var _coord = ol.proj.fromLonLat([d.lon, d.lat]);
      if (_circle.intersectsCoordinate(_coord)) {
        map.getTargetElement().style.cursor = 'pointer';
        document.getElementById('popup').innerText = d.n;
        popup.setPosition(_coord);
        break;
      } else {
        map.getTargetElement().style.cursor = 'default';
        popup.setPosition(null);
      }
    }
  }
});

2)大量数据的检索如何优化? 针对大量数据的检索,在处理的时候做了前端的分区缓存,在通过经纬度去查找对应的分区位置,再去找里面的数据。实现代码如下:

代码语言:javascript
复制
  // 1、核心函数,通过经纬度计算分区位置
function getLonLatIndex(lon, lat) {
  var index = -1;
  // 在边界内
  if(ol.extent.containsCoordinate(chinaB, [lon, lat])) {
    var idxX = (lon - chinaB[0]) / deltLon,
      idxY = (lat - chinaB[1]) / deltLat;
    index = Math.floor(idxY) * numLon + Math.floor(idxX);
  }
  return index;
}

// 2、缓存数据
for(var i = 0;i<data.length;i++) {
  var d = data[i];
  var idx = getLonLatIndex(d.lon, d.lat);
  if(idx !== -1) {
    dataCache[idx].data.push(d);
  }
}

实现源码

代码语言:javascript
复制
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Title</title>
  <link rel="stylesheet" href="https://openlayers.org/en/v4.6.5/css/ol.css" type="text/css">
  <link rel="stylesheet" href="css/demo.css" type="text/css">
</head>
<body>
<div id="map"></div>
<div id="popup"></div>
<script src="http://libs.baidu.com/jquery/2.0.0/jquery.min.js"></script>
<script src="https://openlayers.org/en/v4.6.5/build/ol.js"></script>
<script src="js/demo.js"></script>
<script>
  var vec_w = getWmsLayer("light");
  var data = [];
  var chinaB = [60, 0, 140, 55];
  var deltLon = 8,
    deltLat = 5.5;
  var numLon = (chinaB[2] - chinaB[0]) / deltLon,
    numLat = (chinaB[3] - chinaB[1]) / deltLat;
  var dataCache = [];
  // 数据从左下角开始
  for(var j =0;j<numLat;j++) {
    var maxLat = chinaB[1] + (j+1)*deltLat,
      minLat = chinaB[1] + j*deltLat;
    for(var i =0;i<numLon;i++) {
      var maxLon = chinaB[0] + (i+1)*numLon,
        minLon = chinaB[0] + i*numLon;
      var dCache = {
        xmin: minLon,
        xmax: maxLon,
        ymin: minLat,
        yMax: maxLat,
        data: []
      };
      dataCache.push(dCache);
    }
  }
  var r = 3;
  var map = new ol.Map({
    controls: ol.control.defaults({
      attribution: false
    }),
    target: 'map',
    layers: [vec_w],
    view: new ol.View({
      center: ol.proj.fromLonLat([98.633, 31.607]),
      zoom:4,
      minZoom:0,
      maxZoom:18
    })
  });

  var popup = new ol.Overlay({
    element: document.getElementById('popup'),
    position: null,
    positioning: 'center-left',
    offset: [16, 0]
  });
  map.addOverlay(popup);

  var imageLayer = new ol.layer.Image({
    source: null,
    opacity: 0.85
  });
  map.addLayer(imageLayer);
  
  map.on('click', function (e) {
    var coord = e.coordinate;
    var res = map.getView().getResolution();
    var _radius = res * r;
    var _circle = new ol.geom.Circle(coord, _radius);
    var lonlat = ol.proj.toLonLat(coord);
    var index = getLonLatIndex(lonlat[0], lonlat[1]);
    if(index !== -1) {
      var data = dataCache[index].data;
      for (var i = 0; i < data.length; i++) {
        var d = data[i];
        var _coord = ol.proj.fromLonLat([d.lon, d.lat]);
        if (_circle.intersectsCoordinate(_coord)) {
          console.log(d);
          break;
        }
      }
    }
  });

  map.on('pointermove', function (e) {
    var coord = e.coordinate;
    var res = map.getView().getResolution();
    var _radius = res * r;
    var _circle = new ol.geom.Circle(coord, _radius);
    var lonlat = ol.proj.toLonLat(coord);
    var index = getLonLatIndex(lonlat[0], lonlat[1]);
    if(index !== -1) {
      var data = dataCache[index].data;
      for (var i = 0; i < data.length; i++) {
        var d = data[i];
        var _coord = ol.proj.fromLonLat([d.lon, d.lat]);
        if (_circle.intersectsCoordinate(_coord)) {
          map.getTargetElement().style.cursor = 'pointer';
          document.getElementById('popup').innerText = d.n;
          popup.setPosition(_coord);
          break;
        } else {
          map.getTargetElement().style.cursor = 'default';
          popup.setPosition(null);
        }
      }
    }
  });

  function canvasFunction(extent, res, pixelRatio, size) {
    var mapSize = map.getSize();
    var zoom = map.getView().getZoom();
    r = zoom * 0.6;
    r = r<3 ? 3: r;
    r = r > 10 ? 10: r;
    var xOff = (size[0] - mapSize[0]) / 2,
      yOff = (size[1] - mapSize[1]) / 2;
    var canvas = document.createElement('canvas');
    canvas.setAttribute('width', size[0]);
    canvas.setAttribute('height', size[1]);
    var ctx = canvas.getContext('2d');
    for(var i = 0;i<data.length;i++) {
      var d = data[i];
      ctx.fillStyle = getValueColor(d["pre_1h"]);
      ctx.strokeStyle = 'rgba(0,0,0,0.2)';
      ctx.strokeWidth = 1;
      ctx.beginPath();
      var coord = ol.proj.fromLonLat([d["lon"], d["lat"]]);
      var pixel = map.getPixelFromCoordinate(coord);
      // x, y, r, start, end
      ctx.arc(pixel[0] + xOff, pixel[1] + yOff, r, 0, 2*Math.PI);
      ctx.fill();
      ctx.stroke();
    }
    return canvas;
  }
  
  function getLonLatIndex(lon, lat) {
    var index = -1;
    // 在边界内
    if(ol.extent.containsCoordinate(chinaB, [lon, lat])) {
      var idxX = (lon - chinaB[0]) / deltLon,
        idxY = (lat - chinaB[1]) / deltLat;
      index = Math.floor(idxY) * numLon + Math.floor(idxX);
    }
    return index;
  }

  $.get("datas/data.json", (res) => {
    data = res.data;
    // data = getRandomData();
    var source = new ol.source.ImageCanvas({
        canvasFunction: canvasFunction
    });
    imageLayer.setSource(source);

    for(var i = 0;i<data.length;i++) {
      var d = data[i];
      var idx = getLonLatIndex(d.lon, d.lat);
      if(idx !== -1) {
        dataCache[idx].data.push(d);
      }
    }
  })
</script>
</body>
</html>
本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2019年08月14日,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 概述
  • 实现效果
  • 实现分析
  • 实现源码
相关产品与服务
流计算 Oceanus
流计算 Oceanus 是大数据产品生态体系的实时化分析利器,是基于 Apache Flink 构建的企业级实时大数据分析平台,具备一站开发、无缝连接、亚秒延时、低廉成本、安全稳定等特点。流计算 Oceanus 以实现企业数据价值最大化为目标,加速企业实时化数字化的建设进程。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档