leaflet距离测量

647 阅读3分钟

需求

需要实现实时测距的功能,之前使用leaflet-Geoman的编辑功能,开启编辑之后会把展示的内容转换为点与线段的形式

image.png 需要根据这种实现点击上面的两个点,获取这两个点之间的距离

解决

(注:使用的社区版,很多功能使用不了)

  • 初次尝试

    最开始是在查找这个组件处于编辑状态时的标记点是否有点击事件,找了很久没有发现这个的点击事件

  • 自己动手

    没找到点击事件只能自己想办法了,既然插件的没有那我自己写一套

  1. 实现展示效果

    具体思路就是把真是的图层暂时隐藏,生成一个用于测量的图层

// 用来存储自己创建的线与标记点
const testData = L.layerGroup();
const ranging = () => {
  map.removeLayer(layerContainer); // 移除地图上的 layerContainer 图层,这是移除展示的真实数据
  map.removeLayer(testData); // 移除之前的测试数据图层

// 这是后端传递过来用于渲染的数据
  const codes = JSON.parse(riverInfo.value.vector);
  const coordinates = codes.geometry.coordinates[0]; // 提取坐标数组

  // 创建一个 Polyline 来连接所有标记点
  const polyline = L.polyline(coordinates.map(coord => [coord[1], coord[0]]), {
    color: '#5079ea', // 线的颜色
    weight: 2,    // 线的宽度
    opacity: 0.7, // 线的透明度
  }).addTo(map);

  // 创建标记点并绑定点击事件
  coordinates.forEach(coords => {
    const marker = L.marker([coords[1], coords[0]], {
      icon: L.divIcon({
        className: 'custom-marker',
        iconSize: [20, 20],
        html: `<div style="background-color: #fff; width: 10px; height: 10px; border-radius: 50%;"></div>`,
      }),
      pmIgnore: true, // 忽略 Leaflet-Geoman 的编辑功能
    }).addTo(testData);
    // 为每个 Marker 绑定点击事件
    marker.on('click', (e) => {
      const { lat, lng } = e.target.getLatLng();
      // 触发点击事件
      toggleHighlightMarker(e.target, lat, lng);
    });
  });

  testData.addTo(map); // 将标记点添加到地图
};

2.点击事件处理

这里主要的逻辑是,创建一个容器存储点击的标记点数据,只存储两个标记点,当已经有两个标记点时再次点击,把后面加入的标记点替换掉第二个标记点,当点击已经存储的标记点则把这个标记点移除

// 存储高亮的标记点(最多两个)
const highlightedMarkers = ref([]);

// 切换标记点的高亮状态
function toggleHighlightMarker(marker, lat, lng) {
  const index = highlightedMarkers.value.indexOf(marker);
  if (index !== -1) {
    // 如果已经高亮,取消高亮并移除标记点
    highlightedMarkers.value.splice(index, 1);
    marker.setIcon(L.divIcon({
      className: 'custom-marker',
      iconSize: [20, 20],
      html: `<div style="background-color: #fff; width: 10px; height: 10px; border-radius: 50%;"></div>`,
    }));
    removeSelectedPoint(lat, lng); // 移除坐标点
  } else {
    // 如果未高亮,处理高亮逻辑
    if (highlightedMarkers.value.length >= 2) {
      // 如果已经有两个高亮的标记点,移除最后一个高亮的标记点
      const lastHighlightedMarker = highlightedMarkers.value.pop();
      const { lat: lastLat, lng: lastLng } = lastHighlightedMarker.getLatLng();
      lastHighlightedMarker.setIcon(L.divIcon({
        className: 'custom-marker',
        iconSize: [20, 20],
        html: `<div style="background-color:#fff; width: 10px; height: 10px; border-radius: 50%;"></div>`,
      }));
      removeSelectedPoint(lastLat, lastLng); // 移除最后一个高亮标记点的坐标
    }

    // 添加新点击的标记点
    highlightedMarkers.value.push(marker);
    marker.setIcon(L.divIcon({
      className: 'highlighted-marker',
      iconSize: [25, 25],
      html: `<div style="background-color: #ff7043; width: 15px; height: 15px; border-radius: 50%; border: 2px solid white;"></div>`,
    }));
    addSelectedPoint(lat, lng); // 添加新标记点的坐标
  }
}
// 添加选中的点
function addSelectedPoint(lat, lng) {
  const point = { lat, lng };
  if (!selectedPoints.value.some((p) => p.lat === lat && p.lng === lng)) {
    selectedPoints.value.push(point); // 避免重复添加
    getPointsBetweenMarkers(JSON.parse(riverInfo.value.vector).geometry.coordinates[0])
  }
}
// 移除选中的坐标点
function removeSelectedPoint(lat, lng) {
  const index = selectedPoints.value.findIndex(p => p.lat === lat && p.lng === lng);
  if (index !== -1) {
    selectedPoints.value.splice(index, 1);
  }
}
  1. 获取这两个点之间的其他点(因为不是直线是弯曲点,这里使用的turf的计算api来实现这个功能)

    这应该是是这个功能实现的最大难题,需要获取其他的点

function getPointsBetweenMarkers(coordinates) {
  if (highlightedMarkers.value.length < 2) {
    console.log("需要至少两个高亮标记点来获取中间的点");
    return [];
  }

  // 获取两个高亮标记点的坐标
  const firstMarker = highlightedMarkers.value[0];
  const secondMarker = highlightedMarkers.value[1];

  const firstPoint = firstMarker.getLatLng();
  const secondPoint = secondMarker.getLatLng();

  // 找到这两个点在原始坐标数组中的索引
  let firstIndex = coordinates.findIndex(([lng, lat]) =>
    Math.abs(lng - firstPoint.lng) < 0.0001 && Math.abs(lat - firstPoint.lat) < 0.0001
  );

  let secondIndex = coordinates.findIndex(([lng, lat]) =>
    Math.abs(lng - secondPoint.lng) < 0.0001 && Math.abs(lat - secondPoint.lat) < 0.0001
  );

  if (firstIndex === -1 || secondIndex === -1) {
    console.error("未找到高亮标记点的坐标");
    return [];
  }

  // 确保 firstIndex < secondIndex
  if (firstIndex > secondIndex) {
    [firstIndex, secondIndex] = [secondIndex, firstIndex];
  }

  // 提取两个标记点之间的所有点
  const pointsBetween = coordinates.slice(firstIndex, secondIndex + 1);
  const result = turf.lineString([...pointsBetween])
  const Length = turf.length(result).toFixed(2);
  return Length;
}

至此为止,这个功能大部分实现,剩下的逻辑需要根据实际情况进行修改