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

2019-08-29 11:26:53 浏览数 (1)

概述

在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>

0 人点赞