ECharts 地图 —— 绘制出指定坐标100公里内的地图区块

28 阅读4分钟

在使用 ECharts 进行地图开发时,如果你想要圈定地图的显示范围,如:给一个坐标,绘制出周边100公里内的地图。展示出来,并在地图上绘制出范围圈。并实现范围圈跟随缩放功能,缩放范围。

项目引用

echarts 地图处理,axios 数据加载,这里就不赘述了。

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <title>一定范围内的区域地图</title>
    <style>
      * {
        margin: 0;
        padding: 0;
      }

      #main {
        margin: 50px auto;
      }
    </style>
  </head>
  <body>
    <div id="main" style="width: 1100px; height: 1100px;background-color:rgba(0, 0, 0, 0.01)"></div>
  </body>
  <script src="./js/axios.min.js"></script>
  <script src="./js/echarts.min.js"></script>
</html>

首先获得地图数据

加载了全国,市级别geo数据。

axios
    .get("https://geo.datav.aliyun.com/areas_v3/bound/100000_full_city.json")
    .then((chinaJson) => {
      const mGeoArr = chinaJson.data.features;
      initCharts(chinaJson.data)
    });

地图渲染

把geo数据渲染到屏幕显示

  var myChart = echarts.init(document.getElementById("main"));
  // 存储配置实例
  let option = null;
  // 坐标点
  const mPoint = { lat: 116.418757, lon: 39.917544 };

  // 地图实例
  function initCharts(zhongguo) {
    const markPointData = [{ name: '东城区', coord: [mPoint.lat, mPoint.lon] }];
    option = {
      series: [{
        type: 'map',
        map: 'geo',
        roam: true,
        zoom: 0.8,
        label: {
          normal: {
            show: true,
            textStyle: {
              color: "#ccc",
            },
          },
          emphasis: {
            textStyle: {
              color: "#f00",
            },
          },
        },
        markPoint: {
          symbol: 'circle',
          symbolSize: 240,
          itemStyle: {
            color: 'rgba(255,0,0,0.3)',
            borderColor: '#f00'
          },
          data: markPointData
        },
      }]
    };
    echarts.registerMap("geo", zhongguo);

    myChart.setOption(option);
  }

计算两个坐标点,之间距离

  function calculateDistance(point1, point2) {
    const earthRadius = 6378; // 地球半径(千米)

    const toRadians = (degrees) => {
      return degrees * Math.PI / 180;
    };

    const deltaLat = toRadians(point2.lat - point1.lat);
    const deltaLon = toRadians(point2.lon - point1.lon);

    const a = Math.sin(deltaLat / 2) * Math.sin(deltaLat / 2) +
      Math.cos(toRadians(point1.lat)) * Math.cos(toRadians(point2.lat)) *
      Math.sin(deltaLon / 2) * Math.sin(deltaLon / 2);
    const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));

    const distance = earthRadius * c;
    return distance;
  }

根据坐标和geo数据,计算出在范围圈内的区块

  // 最大距离坐标,后面用到
  let mMaxPoint = {
    dis: 0,
    point: []
  }
  function processingData(dataArr, distance) {
    let mArr = [];
    dataArr.forEach((ele, index) => {
      const mJWDArr = ele.geometry.coordinates;
      let mMaxLat = [], mMaxLon = [], mMinLat = [], mMinLon = [];
      mJWDArr.forEach((item, ind) => {
        item.forEach((cItem, cInd) => {
          if (Array.isArray(cItem[0])) {
            cItem.forEach((ccItem, ccInd) => {
              if (mMaxLat.length <= 0) {
                mMaxLat = ccItem;
                mMaxLon = ccItem;
                mMinLat = ccItem;
                mMinLon = ccItem;
              } else {
                if (ccItem[0] > mMaxLat[0]) {
                  mMaxLat = ccItem;
                }
                if (ccItem[1] > mMaxLat[1]) {
                  mMaxLon = ccItem;
                }
                if (ccItem[0] < mMinLat[0]) {
                  mMinLat = ccItem;
                }
                if (ccItem[1] < mMinLon[1]) {
                  mMinLon = ccItem;
                }
              }
            })
          } else {
            if (mMaxLat.length <= 0) {
              mMaxLat = cItem;
              mMaxLon = cItem;
              mMinLat = cItem;
              mMinLon = cItem;
            } else {
              if (cItem[0] > mMaxLat[0]) {
                mMaxLat = cItem;
              }
              if (cItem[1] > mMaxLat[1]) {
                mMaxLon = cItem;
              }
              if (cItem[0] < mMinLat[0]) {
                mMinLat = cItem;
              }
              if (cItem[1] < mMinLon[1]) {
                mMinLon = cItem;
              }
            }
          }
        });
      });
      const mCDArr = [calculateDistance(mPoint, { lat: mMaxLat[0], lon: mMaxLat[1] }), calculateDistance(mPoint, { lat: mMaxLon[0], lon: mMaxLon[1] }), calculateDistance(mPoint, { lat: mMinLat[0], lon: mMinLat[1] }), calculateDistance(mPoint, { lat: mMinLon[0], lon: mMinLon[1] })];
      // 求出范围跨度最大的坐标
      mCDArr.forEach((item, ind) => {
        if (item > mMaxPoint.dis) {
          mMaxPoint.dis = item;
          if (ind === 0) {
            mMaxPoint.point = mMaxLat;
          } else if (ind === 1) {
            mMaxPoint.point = mMaxLon;
          } else if (ind === 2) {
            mMaxPoint.point = mMinLat;
          } else if (ind === 3) {
            mMaxPoint.point = mMinLon;
          }
        }
      });
      // 设置范围内的区块
      const minValue = Math.min.apply(null, mCDArr);
      if (minValue <= distance) {
        mArr.push(ele);
      }
    });

    return mArr;
  }

调整数据加载

  ……
  const mGeoArr = chinaJson.data.features;
  const mArrFeat = processingData(mGeoArr, 100);
  initCharts({ type: "FeatureCollection", features: mArrFeat });

计算平面两点距离

  function calculateDistanceCanvas(point1, point2) {
    const dx = point2.x - point1.x;
    const dy = point2.y - point1.y;
    const distance = Math.sqrt(dx * dx + dy * dy);
    return distance;
  }

计算100公里在屏幕上的距离

    // geo坐标转屏幕坐标
    var pointInPixel = myChart.convertToPixel({
      seriesIndex: 0, // 地图系列索引,通常为0
      coordSys: 'series'
    }, [mPoint.lat, mPoint.lon]);
    var pointInPixel2 = myChart.convertToPixel({
      seriesIndex: 0,
      coordSys: 'series'
    }, mMaxPoint.point);
    // 平面两点距离
    var lenPx = calculateDistanceCanvas({ x: pointInPixel[0], y: pointInPixel[1] }, { x: pointInPixel2[0], y: pointInPixel2[1] });
    mLent = (lenPx / mMaxPoint.dis) * 100;
    myChart.setOption({
      series: [{
        markPoint: {
          symbolSize: mLent
        }
      }]
    });

缩放时,markPoint画的范围不会缩放

上面用markPoint 在地图上画的范围,不会随地图缩放;所以我们要手动监听缩放实现

    // 缩放范围圈功能
    // 监听缩放
    let mLent = 100;
    myChart.on('georoam', function (event) {
      mLent = mLent * event.zoom;
      myChart.setOption({
        series: [{
          markPoint: {
            symbolSize: mLent
          }
        }]
      });
    });

完整代码

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8" />
  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  <meta name="referrer" content="no-referrer" />
  <title>一定范围内的区域地图</title>
  <style>
    * {
      margin: 0;
      padding: 0;
    }

    #main {
      margin: 50px auto;
    }
  </style>
</head>

<body>
  <div id="main" style="width: 1100px; height: 1100px;background-color:rgba(0, 0, 0, 0.01)"></div>
</body>

<script src="./js/axios.min.js"></script>
<script src="./js/echarts.min.js"></script>
<script>
  function calculateDistance(point1, point2) {
    const earthRadius = 6378; // 地球半径(千米)

    const toRadians = (degrees) => {
      return degrees * Math.PI / 180;
    };

    const deltaLat = toRadians(point2.lat - point1.lat);
    const deltaLon = toRadians(point2.lon - point1.lon);

    const a = Math.sin(deltaLat / 2) * Math.sin(deltaLat / 2) +
      Math.cos(toRadians(point1.lat)) * Math.cos(toRadians(point2.lat)) *
      Math.sin(deltaLon / 2) * Math.sin(deltaLon / 2);
    const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));

    const distance = earthRadius * c;
    return distance;
  }

  // 最大距离坐标,后面用到
  let mMaxPoint = {
    dis: 0,
    point: []
  }
  function processingData(dataArr, distance) {
    let mArr = [];
    dataArr.forEach((ele, index) => {
      const mJWDArr = ele.geometry.coordinates;
      let mMaxLat = [], mMaxLon = [], mMinLat = [], mMinLon = [];
      mJWDArr.forEach((item, ind) => {
        item.forEach((cItem, cInd) => {
          if (Array.isArray(cItem[0])) {
            cItem.forEach((ccItem, ccInd) => {
              if (mMaxLat.length <= 0) {
                mMaxLat = ccItem;
                mMaxLon = ccItem;
                mMinLat = ccItem;
                mMinLon = ccItem;
              } else {
                if (ccItem[0] > mMaxLat[0]) {
                  mMaxLat = ccItem;
                }
                if (ccItem[1] > mMaxLat[1]) {
                  mMaxLon = ccItem;
                }
                if (ccItem[0] < mMinLat[0]) {
                  mMinLat = ccItem;
                }
                if (ccItem[1] < mMinLon[1]) {
                  mMinLon = ccItem;
                }
              }
            })
          } else {
            if (mMaxLat.length <= 0) {
              mMaxLat = cItem;
              mMaxLon = cItem;
              mMinLat = cItem;
              mMinLon = cItem;
            } else {
              if (cItem[0] > mMaxLat[0]) {
                mMaxLat = cItem;
              }
              if (cItem[1] > mMaxLat[1]) {
                mMaxLon = cItem;
              }
              if (cItem[0] < mMinLat[0]) {
                mMinLat = cItem;
              }
              if (cItem[1] < mMinLon[1]) {
                mMinLon = cItem;
              }
            }
          }
        });
      });
      const mCDArr = [calculateDistance(mPoint, { lat: mMaxLat[0], lon: mMaxLat[1] }), calculateDistance(mPoint, { lat: mMaxLon[0], lon: mMaxLon[1] }), calculateDistance(mPoint, { lat: mMinLat[0], lon: mMinLat[1] }), calculateDistance(mPoint, { lat: mMinLon[0], lon: mMinLon[1] })];
      // 求出范围跨度最大的坐标
      mCDArr.forEach((item, ind) => {
        if (item > mMaxPoint.dis) {
          mMaxPoint.dis = item;
          if (ind === 0) {
            mMaxPoint.point = mMaxLat;
          } else if (ind === 1) {
            mMaxPoint.point = mMaxLon;
          } else if (ind === 2) {
            mMaxPoint.point = mMinLat;
          } else if (ind === 3) {
            mMaxPoint.point = mMinLon;
          }
        }
      });
      // 设置范围内的区块
      const minValue = Math.min.apply(null, mCDArr);
      if (minValue <= distance) {
        mArr.push(ele);
      }
    });

    return mArr;
  }

  function calculateDistanceCanvas(point1, point2) {
    const dx = point2.x - point1.x;
    const dy = point2.y - point1.y;
    const distance = Math.sqrt(dx * dx + dy * dy);
    return distance;
  }
</script>
<script>
  // 显示坐标点,一定范围内的区域地图

  var myChart = echarts.init(document.getElementById("main"));

  // 存储实例
  let option = null;
  const mPoint = { lat: 116.418757, lon: 39.917544 };
  axios
    .get("https://geo.datav.aliyun.com/areas_v3/bound/100000_full_city.json")
    .then((chinaJson) => {
      // initCharts(chinaJson.data);
      const mGeoArr = chinaJson.data.features;
      const mArrFeat = processingData(mGeoArr, 100);
      initCharts({ type: "FeatureCollection", features: mArrFeat });
    });


  // 地图实例
  function initCharts(zhongguo) {
    const markPointData = [{ name: '东城区', coord: [mPoint.lat, mPoint.lon] }];

    option = {
      series: [{
        type: 'map',
        map: 'geo',
        roam: true,
        zoom: 0.8,
        label: {
          normal: {
            show: true,
            textStyle: {
              color: "#ccc",
            },
          },
          emphasis: {
            textStyle: {
              color: "#f00",
            },
          },
        },
        markPoint: {
          symbol: 'circle',
          symbolSize: 100,
          itemStyle: {
            color: 'rgba(255,0,0,0.3)',
            borderColor: '#f00'
          },
          data: markPointData
        },
      }]
    };
    echarts.registerMap("geo", zhongguo);

    myChart.setOption(option);

    // 缩放范围圈功能
    // 监听缩放
    let mLent = 100;
    myChart.on('georoam', function (event) {
      mLent = mLent * event.zoom;
      myChart.setOption({
        series: [{
          markPoint: {
            symbolSize: mLent
          }
        }]
      });
    });
    // geo坐标转屏幕坐标
    var pointInPixel = myChart.convertToPixel({
      seriesIndex: 0, // 地图系列索引,通常为0
      coordSys: 'series'
    }, [mPoint.lat, mPoint.lon]);
    var pointInPixel2 = myChart.convertToPixel({
      seriesIndex: 0,
      coordSys: 'series'
    }, mMaxPoint.point);
    // 平面两点距离
    var lenPx = calculateDistanceCanvas({ x: pointInPixel[0], y: pointInPixel[1] }, { x: pointInPixel2[0], y: pointInPixel2[1] });
    mLent = (lenPx / mMaxPoint.dis) * 100;
    myChart.setOption({
      series: [{
        markPoint: {
          symbolSize: mLent
        }
      }]
    });
  }

</script>

</html>