vue + echarts + 百度地圖 實現散點圖框選


需求:根據坐標將所有零售櫃用散點顯示在地圖上,散點大小代表櫃子銷售額大小。可以連續框選櫃子,在地圖右側顯示所選櫃子銷售額對比曲線。

完成效果圖:

 

 

1.確定方案

這個需求的難點主要在於連續框選,且要獲得框選的散點信息。一開始查資料發現 echarts 有個區域選擇組件 brush(https://echarts.apache.org/zh/option.html#brush),可以實現連續框選。踩了很多坑勉強實現功能后發現還是有些問題,比如鼠標拖拽跟框選事件沖突,拖拽或放大地圖選框會跟着移動不能保持在原位置……后來改變方案,利用百度地圖 BMapLib 基礎類的 DrawingManager 庫實現框選。

2. 引入百度地圖和工具庫

index.html 中加入

<!-- 百度地圖 -->
<script type="text/javascript" src="https://api.map.baidu.com/api?v=3.0&ak=你的密鑰"></script>

<!-- library 基礎類 -->
<script type="text/javascript" src="https://api.map.baidu.com/library/GeoUtils/1.2/src/GeoUtils_min.js"></script>

<!-- DrawingManager庫-->
<script type="text/javascript"   src="https://api.map.baidu.com/library/DrawingManager/1.4/src/DrawingManager_min.js"></script>

webpack.base.conf.js 中加入

externals: {
  "BMapLib": "BMapLib"
}

在組件中引入

import BMapLib from 'BMapLib'

3. echarts 配置

this.bmap = echarts.init(document.getElementById("map"));

配置分為幾部分,首先是地圖部分(配置的詳細說明可以自行查找,我在官網上沒有找到。地圖與散點圖配置可以參考官方實例 https://echarts.apache.org/examples/zh/editor.html?c=map-polygon):

bmap: {
  center: [104.114129, 37.550339],
  zoom: 5,
  roam: true,
  mapStyle: {
    styleJson: [
      {
        featureType: "land",
        elementType: "geometry",
        stylers: {
          color: "#f5f6f7ff",
        },
      },
      {
        featureType: "water",
        elementType: "geometry",
        stylers: {
          color: "#c4d7f5ff",
        },
      },
      {
        featureType: "green",
        elementType: "geometry",
        stylers: {
          color: "#dcf2d5ff",
        },
      },
      {
        featureType: "highway",
        elementType: "all",
        stylers: { visibility: "off" },
      },
    ],
  },
}

散點圖部分:

var data = [
    {name: 'name1', value: 9},
    {name: 'name2', value: 12},
    {name: 'name3', value: 12}
]

var geoCoordMap = {
    'name1':[121.15,31.89],
    'name2':[109.781327,39.608266],
    'name3':[120.38,37.35]
}

var convertData = function (data) {
    var res = [];
    for (var i = 0; i < data.length; i++) {
        var geoCoord = geoCoordMap[data[i].name];
        if (geoCoord) {
            res.push({
                name: data[i].name,
                value: geoCoord.concat(data[i].value)
            });
        }
    }
    return res;
};

// 散點圖配置
series: [
  {
    type: "scatter",
    coordinateSystem: "bmap",
    data: this.convertData(this.data),
    symbolSize: function (val) {
        return val[2] / 10;
    }
  }
]

這里涉及到一個問題,散點圖的大小怎么根據 value 設置,用上面的 symboSize 雖然能區別大小,但不好控制,如果 value 過大或過小顯示在地圖上都會很難看。最好的方法是給一個范圍,讓散點大小在這個范圍里形成映射。數學不好就直接用工具了,D3js有個線性比例尺可以實現該功能(https://segmentfault.com/a/1190000011006780

在 index.html 引入 D3js

<script src="https://d3js.org/d3.v5.js"></script>

symbolSize 改寫如下

// 將所有 value 值放在數組 valueArr 里
var valueArr = [];
data.forEach((item) => {
    valueArr.push(item.value);
});

symbolSize: (val, params) => {
// 將 valueArr 里的所有值映射到 [10, 40] 這個范圍里
  let scale = d3
    .scaleLinear()
    .domain([
      Math.min(...valueArr),
      Math.max(...valueArr),
    ])
    .range([10, 40]);
  return scale(val[2]);
},

散點是畫好了,下一個問題又來了:給散點加 tooltip,拖拽地圖的時候 tooltip 會跟着地圖一起移動,不能停留在 hover 的點附近,解決辦法參考:https://blog.csdn.net/wooden_people/article/details/89668999

增加 tooltip 設置:

tooltip: {
  trigger: "item",
  formatter: function (params, ticket, callback) {
    return (
      params.value[4] +
      "<br />" +
      params.value[3] +
      "<br />" +
      params.name +
      "<br />" +
      "銷售額: " +
      params.value[2] +
      "元"
    );
  },
  position: function (pos, params, dom, rect, size) {
    var top = $(".BMap_mask").css("top"); //.BMap_mask為固定格式
    var left = $(".BMap_mask").css("left");
    top = top.substring(0, top.length - 2); //去除px單位
    left = left.substring(0, left.length - 2);
    return [pos[0] + 20 - left, pos[1] + 20 - top]; //20可根據需要調整
  },
}

4. 框選功能實現

參考:http://lbsyun.baidu.com/jsdemo.htm#f0_7 

值的注意的是,我們的 bmap 對象並不是通過 new BMap.Map('map') 創建的,而是以bmap屬性來設置的,因此不能直接使用百度地圖api,好在 echarts 提供了獲取 bmap 對象實例的方法 ;

// 獲取 bmap 對象實例
this.bmapModel = this.bmap.getModel().getComponent("bmap").getBMap();

// 設置邊框樣式
var styleOptions = {
  strokeColor: "red", //邊線顏色。
  fillColor: "red", //填充顏色。當參數為空時,圓形將沒有填充效果。
  strokeWeight: 1, //邊線的寬度,以像素為單位。
  strokeOpacity: 0.8, //邊線透明度,取值范圍0 - 1。
  fillOpacity: 0.3, //填充的透明度,取值范圍0 - 1。
  strokeStyle: "solid", //邊線的樣式,solid或dashed。
};

var drawingManager = new BMapLib.DrawingManager(this.bmapModel, {
  isOpen: false, //是否開啟繪制模式
  enableDrawingTool: true, //是否顯示工具欄
  drawingToolOptions: {
    anchor: BMAP_ANCHOR_TOP_RIGHT, //位置
    offset: new BMap.Size(5, 5), //偏離值
    scale: 0.6,
    drawingModes: [BMAP_DRAWING_RECTANGLE],
  },
  rectangleOptions: styleOptions, //矩形的樣式
});

現在就可以框選地圖上的點了!下一步是獲取所框選的點的信息,如何知道哪些點被框住了呢?可以根據點的坐標來判斷,已知每個點的坐標以及選框四個點的坐標,百度接口中有判斷圍欄的功能,即一個點是否在某個矩形中。參考:https://blog.csdn.net/u012539364/article/details/46648147

drawingManager.addEventListener("overlaycomplete", (e) => {
  var pStart = e.overlay.getPath()[3]; //矩形左上角坐標
  var pEnd = e.overlay.getPath()[1]; //矩形右下角坐標
  var pt1 = new BMap.Point(pStart.lng, pStart.lat); //3象限
  var pt2 = new BMap.Point(pEnd.lng, pEnd.lat); //1象限
  var bds = new BMap.Bounds(pt1, pt2); //范圍

  for (let key in geoCoordMap) {
    var pt = new BMap.Point(
      geoCoordMap[key][0],
      geoCoordMap[key][1]
    );
    if (BMapLib.GeoUtils.isPointInRect(pt, bds)) {
    // 將框選的點存儲在 deviceArr 中
      if (this.deviceArr.indexOf(key) === -1) {
        this.deviceArr.push(key);
      }
    }
  }
  // 利用拿到的信息發起請求
  .......
});

至此基本功能就完成了,至於肯定會存在的其他問題就不一一說明了,echarts 不同版本配置不同也挺讓人頭疼的,而且我還沒有找到以前版本的文檔。另外這個框選功能好像不支持移動端,留待后續探究。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM