当前位置:   article > 正文

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

ol-canvas

概述

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

实现效果

实现效果

实现分析

1.效率差异如何得来?

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

2.交互如何实现

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

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);
      }
    }
  }
});
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24

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

  // 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);
  }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20

实现源码

<!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>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166
  • 167
  • 168
  • 169
  • 170
  • 171
  • 172

技术博客
CSDN:http://blog.csdn.NET/gisshixisheng
在线教程
https://edu.csdn.net/course/detail/799
https://edu.csdn.net/course/detail/7471
联系方式

类型内容
qq1004740957
公众号lzugis15
e-mailniujp08@qq.com
webgis群452117357
Android群337469080
GIS数据可视化群458292378

LZUGIS

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/小蓝xlanll/article/detail/728116
推荐阅读
相关标签
  

闽ICP备14008679号