高德地图api实现索引聚合

需求背景

实现地图上的地点的聚合效果

解决效果

在这里插入图片描述

index.vue

<template>
  <div class="map-container">
    <div id="frontPage-map"></div>
  </div>
</template>

<script setup>
import ShenFeatureData from "./shen_features";
import pointsData from "./posData"
import {onMounted, reactive, toRefs} from "vue";

const model = reactive({
  tempData: [],// 有位置信息的数据
  idexCluster: null,// 索引聚合
  addCluster: null,// 点聚合
})
const {tempData, idexCluster, addCluster} = toRefs(model)

// 地图聚合分类
let clusterIndexSet = {
  province: {
    minZoom: 4,
    maxZoom: 6.5,
  },
  name: {
    minZoom: 15,
    maxZoom: 16,
  },
};

const toVillage = (obj) => {
  alert('跳转到该地点',obj)
}

// 地图省界限
const addPolygon = (pos, sheng) => {
  let polygon = new AMap.Polygon({
    path: pos,
    fillColor: 'transparent',
    strokeOpacity: 1,
    strokeColor: '#2b8cbe',
    strokeWeight: 1,
    strokeStyle: 'dashed',
    strokeDasharray: [5, 5],
    extData: sheng,
  });
  polygon.on('mouseover', () => {
    polygon.setOptions({
      fillOpacity: 0.3,
      fillColor: '#7bccc4'
    });
  });
  polygon.on('mouseout', () => {
    polygon.setOptions({
      fillOpacity: 0.5,
      fillColor: 'transparent'
    });
  });
  //创建右键菜单
  let contextMenu = new AMap.ContextMenu();
  contextMenu.addItem("缩放至全国范围,显示所有省份", function () {
    map.setZoomAndCenter(4.5, [108, 39]);
    // 清空图层
    idexCluster.value.setMap(null); // 清除地图上所有聚合
    idexCluster.value = null;
    // 添加新的聚合
    idexCluster.value = new AMap.IndexCluster(window.map, pointsData, {
      renderClusterMarker: renderClusterMarker,
      clusterIndexSet: clusterIndexSet,
    });
    addMarkerCluster(pointsData);
  }, 2);
  // 右键显示菜单
  polygon.on('rightclick', function (e) {
    if (window.map.getZoom() === 4.5) {
      return;
    }
    contextMenu.open(map, e.lnglat);
  });
  // 全球右键回到中心点
  window.map.on('rightclick', function (e) {
    if (window.map.getZoom() === 4.5) {
      return;
    }
    contextMenu.open(map, e.lnglat);
  });
  window.map.add(polygon);
}
// 按索引聚合
const getStyle = (context) => { // 设置样式
  let clusterData = context.clusterData; // 聚合中包含数据
  let index = context.index; // 聚合的条件
  let count = context.count; // 聚合中点的总数
  let color = ["8,60,156", "66,130,198"];
  let indexs = ["province", "name"];
  let i = indexs.indexOf(index["mainKey"]);
  let text = clusterData[0][index["mainKey"]];
  let size = Math.round(10 + Math.pow(count / 2300, 1 / 5) * 60); // 字体大小 todo
  if (index["mainKey"] === 'province') {
    let extra = `<span class="showCount tow">${context.count}</p>`;
    let showTxt = text === '黑龙江省' ? "黑龙江" : text.slice(0, 2);
    text = '<p class="showName towa" title="' + text + '">' + showTxt + "</p>";
    text += extra;
    size = 40;
  } else {
    size = 12 * text.length + 20;
    text = '<span class="mcshow">' + text + "</span>";
  }
  let style = {
    bgColor: "rgba(" + color[i] + ",.8)",
    borderColor: "rgba(" + color[i] + ",1)",
    text: text,
    size: size,
    index: i,
    province: clusterData.province,
    offset: [0, 30],
    color: "#ffffff",
    textAlign: "center",
    cursor: 'pointer',
    boxShadow: "0px 0px 5px rgba(0,0,0,0.8)",
  };
  return style;
}
// 获取坐标
const getPosition = (context) => {
  let key = context.index?.mainKey || "province";
  let dataItem = context.clusterData && context.clusterData[0];
  let districtName = dataItem[key];
  if (!getShenCenter(districtName)) {
    return null;
  }

  let center = getShenCenter(districtName);
  let centerLnglat = new AMap.LngLat(center[0], center[1]);
  return centerLnglat;
}
//自定义聚合点样式
const renderClusterMarker = (context) => {
  let index = context.index; // 聚合的条件
  let marker = context.marker; // 聚合点标记对象
  let styleObj = getStyle(context);
  // 自定义点标记样式
  let div = document.createElement("div");
  div.className = "amap-cluster";
  div.style.backgroundColor = styleObj.bgColor;
  div.style.width = styleObj.size + "px";
  div.style.color = styleObj.color;
  div.style.fontSize = "8px";
  if (index["mainKey"] === 'province') {
    div.style.height = styleObj.size + "px";
    div.style.border = "solid 1px " + styleObj.borderColor;
    div.style.boxShadow = styleObj.boxShadow;
    // 自定义点击事件
    context.marker.on("click", function () {
      let char = marker.getContent().innerText.slice(0, 2);
      model.tempData = pointsData.filter(item => item.province.indexOf(char) !== -1);
      idexCluster.value.setMap(null); // 清除地图上所有聚合
      idexCluster.value = null;
      // 添加新的聚合
      idexCluster.value = new AMap.IndexCluster(window.map, model.tempData, {
        renderClusterMarker: renderClusterMarker,
        clusterIndexSet: clusterIndexSet,
      });
      // 跳到当前位置视角
      window.map.setZoomAndCenter(8, getPosition(context));
    });
  } else {
    div.style.width = "40px";
    div.style.height = "50px";
    div.style.color = "transparent";
    div.style.overflow = "hidden";
    div.style.whiteSpace = "nowrap";
    div.style.position = 'relative';
    div.style.top = '-40px';
  }
  div.style.borderRadius = styleObj.size + "px";
  div.innerHTML = styleObj.text;
  div.style.textAlign = styleObj.textAlign;
  context.marker.setContent(div);
  // 自定义聚合点标记显示位置
  let position = getPosition(context);
  if (position) {
    context.marker.setPosition(position);
  }
  context.marker.setAnchor("center");
  let zoom = window.map.getZoom();
  if (zoom == 4.5) {
    window.map.setCenter([108, 39]);
  }
}
// 按点聚合
const addMarkerCluster = (points) => {
  let gridSize = 60;
  let count = points.length;
  let _renderClusterMarker = function (context) {
    let factor = Math.pow(context.count / count, 1 / 18);
    let div = document.createElement('div');
    let Hue = 180 - factor * 180;
    let bgColor = 'hsla(' + Hue + ',100%,40%,0.7)';
    let fontColor = 'hsla(' + Hue + ',100%,90%,1)';
    let borderColor = 'hsla(' + Hue + ',100%,40%,1)';
    let shadowColor = 'hsla(' + Hue + ',100%,90%,1)';
    div.style.backgroundColor = bgColor;
    let size = Math.round(10 + Math.pow(context.count / count, 1 / 5) * 20);
    div.style.width = div.style.height = size + 'px';
    div.style.border = 'solid 1px ' + borderColor;
    div.style.borderRadius = size / 2 + 'px';
    div.style.boxShadow = '0 0 5px ' + shadowColor;
    div.innerHTML = context.count;
    div.style.lineHeight = size + 'px';
    div.style.color = fontColor;
    div.style.fontSize = '12px';
    div.style.textAlign = 'center';
    // 自定义点击事件
    context.marker.on("click", function (e) {
      let zoom = window.map.getZoom();
      window.map.setZoomAndCenter(zoom + 3, e.lnglat);
    });
    context.marker.setOffset(new AMap.Pixel(-size / 2, -size / 2));
    context.marker.setContent(div);
  };
  let _renderMarker = function (context) {
    let clusterData = context.data[0]; // 聚合中包含数据
    let content = document.createElement('div');
    let p = document.createElement('p');
    content.appendChild(p);
    content.style.width = "30px";
    content.style.height = "60px";
    content.style.position = 'relative';
    p.classList.add('tow');
    p.innerText = clusterData.name;
    p.setAttribute('title', clusterData.name);
    p.style.color = "#fff";
    p.style.textAlign = 'center';
    p.style.position = 'absolute';
    p.style.top = '10px';
    p.style.left = '50%';
    p.style.transform = 'translateX(-50%)';
    p.style.padding = '0 5px ';
    p.style.borderRadius = '5px';
    p.style.backgroundColor = 'rgba(112,92,98,.7)';
    p.style.lineHeight = '20px';
    p.style.fontSize = '8px';
    if (clusterData.type == 1) {
      content.style.background = `url('./src/assets/icon/zhen.png') no-repeat 0px 32px/contain`;
    } else {
      content.style.background = `url('./src/assets/icon/cun.png') no-repeat 0px 32px/contain`;
    }
    let offset = new AMap.Pixel(-22, -45);
    // 自定义点击事件
    context.marker.on("click", function () {
      let obj = {
        id: clusterData.id,
        name: clusterData.name,
        province: clusterData.province,
        city: clusterData.city,
        county: clusterData.county,
        visitorVolume: clusterData.visitorVolume,
        imageUrl: clusterData.imageUrl
      };
      toVillage(obj);
    });
    context.marker.setContent(content);
    context.marker.setOffset(offset);
  };
  if (addCluster.value) {
    addCluster.value.setMap(null);
  }
  addCluster.value = new AMap.MarkerCluster(window.map, points, {
    gridSize: gridSize, // 设置网格像素大小
    renderClusterMarker: _renderClusterMarker, // 自定义聚合点样式
    renderMarker: _renderMarker, // 自定义非聚合点样式
  });
}
// 获取省会中心
const getShenCenter = (name) => {
  const features = ShenFeatureData.features;
  const target = features.find(item => item.properties.name === name);
  return target.properties.center;
}

onMounted(() => {
  window.map = new AMap.Map('frontPage-map', { // 创建地图
    center: new AMap.LngLat(108, 39),
    zoom: 4.4,
    zooms: [4.5, 14],
    animateEnable: true,
  });
  // 监听地图层级变化事件
  window.map.on("zoomchange", function () {
    let zoom = window.map.getZoom();
    if (zoom > 6.5) {
      addCluster.value || addMarkerCluster(model.tempData);
    } else {
      if (addCluster.value) {
        addCluster.value.setMap(null);
        addCluster.value = null;
      }
    }
    if (zoom <= 4.8) { // 显示所有的点
      idexCluster.value && idexCluster.value.setMap(null); // 清除地图上所有聚合
      idexCluster.value = null;
      // 添加新的聚合
      idexCluster.value = new AMap.IndexCluster(window.map, pointsData, {
        renderClusterMarker: renderClusterMarker,
        clusterIndexSet: clusterIndexSet,
      });
    }
  });
  // 绘制省界限
  const features = ShenFeatureData.features;
  let rings = [];
  features.forEach((feature) => {
    rings = feature.geometry.coordinates;
    rings.forEach((ring) => {
      addPolygon(ring, feature.properties.name);
    });
  });
});

</script>

<style lang="scss" scoped>
.map-container {
  position: relative;
  width: 100%;
  height: 700px;
  margin-bottom: 20px;
}

// 修改地图样式
::v-deep {
  #frontPage-map {
    width: 100%;
    height: 100%;

    .amap-cluster {
      //文字
      p {
        position: relative;
        //top: 5px;
      }
    }

    .amap-marker {

      .showName {
        width: 35px;
        line-height: 1.1;
        margin: 3px auto 0;
      }
    }
  }

  .tow {
    text-overflow: ellipsis;
    overflow: hidden;
    white-space: nowrap;
  }
}
</style>

posData.json

链接

shen_features.json

链接

视频效果

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

柳晓黑胡椒

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值