【前端实践系列之七】百度地图热力图开发及区域边界线的实现

2,403 阅读3分钟

这是我参与更文挑战的第8天,活动详情查看: 更文挑战 !

👽 概论

前几天和大家分享了Echarts热力图的制作方法,但有个问题在于:Echarts中使用百度地图时,不能很好的控制百度地图的样式,部分百度地图的扩展功能使用也受限。有时能用百度地图解决的,不引入Echarts也挺好。

这次主要实现两个功能:一是实现自定义边界的绘制,二是实现热力图的绘制。

👻 开发准备

  1. 引入百度地图API(常规操作,无需多言);
  2. 引入百度热力图开源库(这个很关键)。
<!DOCTYPE html>
<html lang="zh">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>百度热力地图</title>
    <style>
      html,
      body {
        height: 100%;
        padding: 0;
      }
      #map {
        height: 1200px;
        width: 100%;
      }
    </style>
  </head>

  <body>
    <div id="map"></div>
  </body>
  

  <!-- 百度地图API -->
  <script
    type="text/javascript"
    src="http://api.map.baidu.com/api?v=3.0&ak=xxxxxxxxx"
  ></script>
  <!-- 百度热力图开源库 -->
  <script
    type="text/javascript"
    src="http://api.map.baidu.com/library/Heatmap/2.0/src/Heatmap_min.js"
  ></script>

  <!-- 热力图数据 -->
  <script type="text/javascript" src="./data/jsonArr.js"></script>
  <!-- 区块边界数据 -->
  <script type="text/javascript" src="./data/features.js"></script>
  <!-- 逻辑JS -->
  <script type="text/javascript" src="index.js"></script>
</html>

👽 初始化百度地图

这部分都是基本操作,此处只做简单介绍。

function initMap(domID, centerPoint, styleId) {
  let map = new BMap.Map(domID); // 创建地图实例

  map.centerAndZoom(centerPoint, 15); //设置中心点及缩放级别

  map.setMapStyleV2({ styleId }); //设置样式ID

  map.enableScrollWheelZoom();
  map.enableDragging();
  map.disableDoubleClickZoom();

  return map;
}

window.onload = function () {
  let centerPoint = new BMap.Point(104.084207, 30.695243);
  let styleId = '02f2d7d1f52e7261479d4a9755a8485e';

  let mapInstance = initMap('map', centerPoint, styleId);
}

此阶段效果如图:

image.png

👽 区域边界的绘制

区域边界线实现的原理是借助一系列边界点的坐标,结合百度地图中的多边形类Polygon来实现,但在实践中有两种情况:一种是市、县等行政区域,另一种是类似功能区、商圈这种非行政区划类的区域。

对于第一种,我们一般通过借助百度地图API先获取区划边界数据点,再进行描边绘制; 对于第二种,只需准备好边界点数据即可直接绘制。

···

//自定义边界绘制
function generateBoundary(mapInstance, points, style) {
  let polygon = new BMap.Polygon(points, style); //创建多边形
 
  mapInstance.addOverlay(polygon); //添加多边形

  return polygon;
}


//行政区域边界绘制
function generateDistrictBoundary(mapInstance, districtName, style) {
  let bdary = new BMap.Boundary();
  bdary.get(districtName, function (res) {
    let boundaries = res.boundaries;

    let points = [];
    //之所以用循环,是因为部分行政区存在飞地,区划会分为多块,所以需要遍历生成
    boundaries.forEach(boundary => {
      let polygon = generateBoundary(mapInstance, boundary, style);

      let path = polygon.getPath();
      points = points.concat(path);
    });

  });
}


window.onload = function () {
   ···
  let style = {
    strokeWeight: 3,
    strokeColor: '#2c2a2a',
    strokeStyle: 'dashed',
    fillColor: 'transparent',
  };
  generateDistrictBoundary(mapInstance, '金牛区', style);
  let config = [
    {
      sort: 1,
      fillColor: 'rgb(248,195,201)',
    },
    {
      sort: 2,
      fillColor: 'rgb(158,202,244)',
    },
    {
      sort: 3,
      fillColor: 'rgb(238,253,133)',
    },
    {
      sort: 4,
      fillColor: 'transparent',
    },
  ];
  //features数据结构
  //features:[ { sort: 1, lat: 30.745365, lng: 104.124755 }···]
  config.forEach(obj => {
    let points = features
      .filter(item => item.sort == obj.sort)
      .map(item => new BMap.Point(item.lng, item.lat));

    generateBoundary(mapInstance, points, {
      strokeWeight: 3,
      strokeColor: '#2c2a2a',
      strokeStyle: 'dashed',
      fillColor: obj.fillColor,
      fillOpacity: 0.7,
    });
  });
};

此阶段效果如图(左上角为典型的飞地):

image.png

👽 热力图绘制

热力图的绘制需要借助百度的开源热力图库。完整代码如下:

function initMap(domID, centerPoint, styleId) {
  let mapInstance = new BMap.Map(domID); // 创建地图实例

  mapInstance.centerAndZoom(centerPoint, 15); //设置中心点及缩放级别

  mapInstance.setMapStyleV2({ styleId }); //设置样式ID

  mapInstance.enableScrollWheelZoom();
  mapInstance.enableDragging();
  mapInstance.disableDoubleClickZoom();

  return mapInstance;
}

function generateBoundary(mapInstance, points, style) {
  let polygon = new BMap.Polygon(points, style); //创建多边形

  // 添加覆盖物
  mapInstance.addOverlay(polygon); //增加多边形

  return polygon;
}

function generateHeat(mapInstance, data) {
  const heatmapOverlay = new BMapLib.HeatmapOverlay({ radius: 30 });

  mapInstance.addOverlay(heatmapOverlay);

  heatmapOverlay.setDataSet({
    data, //热力值数据
    max: 10000, // 热力权重最大值
  });
}

function generateDistrictBoundary(mapInstance, districtName, style) {
  let bdary = new BMap.Boundary();
  bdary.get(districtName, function (res) {
    let boundaries = res.boundaries;

    let points = [];
    //之所以用循环,是因为部分行政区存在飞地,区划会分为多块,所以需要遍历生成
    boundaries.forEach(boundary => {
      let polygon = generateBoundary(mapInstance, boundary, style);

      let path = polygon.getPath();
      points = points.concat(path);
    });

    setTimeout(() => generateHeat(mapInstance, jsonArr), 0);
  });
}

window.onload = function () {
  let centerPoint = new BMap.Point(104.084207, 30.695243);
  let styleId = '02f2d7d1f52e7261479d4a9755a8485e';

  let mapInstance = initMap('map', centerPoint, styleId);

  let style = {
    strokeWeight: 3,
    strokeColor: '#2c2a2a',
    strokeStyle: 'dashed',
    fillColor: 'transparent',
  };

  generateDistrictBoundary(mapInstance, '金牛区', style);

  let config = [
    {
      sort: 1,
      fillColor: 'rgb(248,195,201)',
    },
    {
      sort: 2,
      fillColor: 'rgb(158,202,244)',
    },
    {
      sort: 3,
      fillColor: 'rgb(238,253,133)',
    },
    {
      sort: 4,
      fillColor: 'transparent',
    },
  ];
  //features数据结构
  //features:[ { sort: 1, lat: 30.745365, lng: 104.124755 }···]
  config.forEach(obj => {
    let points = features
      .filter(item => item.sort == obj.sort)
      .map(item => new BMap.Point(item.lng, item.lat));

    generateBoundary(mapInstance, points, {
      strokeWeight: 3,
      strokeColor: '#2c2a2a',
      strokeStyle: 'dashed',
      fillColor: obj.fillColor,
      fillOpacity: 0.7,
    });
  });
};

此阶段的效果如图:

image.png

👽 结语

详细的介绍都在代码注释,如有疑惑欢迎交流~