You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何限制Leaflet地图仅在指定公路旅行路线添加Street View标记

如何限制仅在Leaflet路由路线上添加Google街景标记

我制作了挪威夏季公路旅行的Leaflet地图,当前点击地图任意位置都会添加Google街景标记,但希望仅允许在公路旅行路线上点击时才创建该标记。

解决方案

核心思路是获取路由生成的完整路径坐标,计算点击点到路径的最小距离,当距离小于设定阈值(比如50米)时,才触发街景标记添加逻辑。

修改后的完整代码

HTML(无需改动)

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <link rel="stylesheet" href="https://unpkg.com/leaflet/dist/leaflet.css" />
  <link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/npm/slick-carousel@1.8.1/slick/slick.css" />
  <link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/npm/slick-carousel@1.8.1/slick/slick-theme.css" />
  <script src="https://unpkg.com/leaflet/dist/leaflet.js"></script>
  <script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
  <script type="text/javascript" src="https://cdn.jsdelivr.net/npm/slick-carousel@1.8.1/slick/slick.min.js"></script>
  <link rel="stylesheet" href="https://unpkg.com/leaflet-routing-machine/dist/leaflet-routing-machine.css" />
  <script src="https://unpkg.com/leaflet-routing-machine/dist/leaflet-routing-machine.js"></script>
  <style>
  </style>
</head>
<body>
  <div id="map"></div>
</body>
</html>

CSS(无需改动)

body {
  margin: 0;
}

#map {
  width: 100%;
  height: 100vh;
}

.carousel {
  max-width: 300px;
  margin: 10px auto;
}

.carousel img {
  width: 100%;
  height: auto;
}

.geiranger-popup-content {
  max-width: 500px;
  padding: 20px;
}

JavaScript(关键修改部分)

var map = L.map('map').setView([61.9241, 6.7527], 6);
var streetViewMarker = null;
var routeCoordinates = []; // 存储路由路径的坐标数组
var DISTANCE_THRESHOLD = 50; // 阈值:50米内视为在路线上

L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
  attribution: '© OpenStreetMap contributors'
}).addTo(map);

var roadTripCoordinates = [
  [59.9139, 10.7522], // Oslo
  [62.2622, 10.7654], // Tynset
  [62.5949, 9.6926], // Oppdal
  [63.0071, 7.2058], // Atlantic Road
  [62.1040, 7.2054] // Geiranger
];

// 初始化轮播图函数
function initSlickCarousel(markerId, images) {
  $(`#${markerId}_carousel`).slick({
    infinite: true,
    slidesToShow: 1,
    slidesToScroll: 1,
    dots: true,
    arrows: true
  });

  images.forEach(img => {
    $(`#${markerId}_carousel`).slick('slickAdd', `<div><img src="${img}" alt="Image"></div>`);
  });
}

// 目的地数据
var destinations = [{
    coordinates: [59.9139, 10.7522],
    name: 'Oslo',
    info: "../07/2023 : Start of the road-trip",
    images: ['https://www.ecologie.gouv.fr/sites/default/files/styles/standard/public/Oslo%2C%20Norvege_AdobeStock_221885853.jpeg?itok=13d8oQbU', 'https://via.placeholder.com/300', 'https://via.placeholder.com/300']
  },
  {
    coordinates: [62.2622, 10.7654],
    name: 'Tynset',
    info: "../07/2023 : Fly-fishing spot 1",
    images: ['https://www.czechnymph.com/data/web/gallery/fisheries/norway/glommahein/Kvennan_Fly_Fishing_20.jpg', 'https://via.placeholder.com/300', 'https://via.placeholder.com/300']
  },
  {
    coordinates: [62.5949, 9.6926],
    name: 'Oppdal',
    info: "../07/2023 : Awesome van spot for the night",
    images: ['https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcSFRtpLlHWr8j6S2jNStnq6_Z9qBe0jWuFH8Q&usqp=CAU', 'https://via.placeholder.com/300', 'https://via.placeholder.com/300']
  },
  {
    coordinates: [63.0071, 7.2058],
    name: 'Atlantic Road',
    info: "../07/2023 : Fjord fishing",
    images: ['https://images.locationscout.net/2021/04/atlantic-ocean-road-norway.jpg?h=1100&q=83', 'https://via.placeholder.com/300', 'https://via.placeholder.com/300']
  },
  {
    coordinates: [62.1040, 7.2054],
    name: 'Geiranger',
    info: "../07/2023 : Hiking 1",
    images: ['https://www.fjordtours.com/media/10968/nicola_montaldo-instagram-26th-may-2021-0717-utc.jpeg?anchor=center&mode=crop&width=1120&height=1120&rnd=133209254300000000&slimmage=True', 'https://via.placeholder.com/300', 'https://via.placeholder.com/300']
  }
];

// 创建路由控件并监听route事件获取路径
var routingControl = L.Routing.control({
  waypoints: roadTripCoordinates.map(coord => L.latLng(coord[0], coord[1])),
  router: L.Routing.mapbox('MAP_BOX_KEY'), // 替换为你的Mapbox密钥
  draggableWaypoints: false,
  addWaypoints: false,
  lineOptions: {
    styles: [{
      color: 'brown',
      opacity: 0.7,
      weight: 2
    }]
  }
}).addTo(map);

// 监听路由生成事件,保存路径坐标
routingControl.on('route', function(e) {
  // 获取第一条路线的所有坐标点
  routeCoordinates = e.route.coordinates;
});

// 移除路由控件的默认UI
var routingContainer = document.querySelector('.leaflet-routing-container');
if (routingContainer) {
  routingContainer.parentNode.removeChild(routingContainer);
}

// 添加目的地标记
destinations.forEach(function(destination) {
  var marker = L.marker(destination.coordinates).addTo(map);
  var markerId = destination.name.replace(' ', '_');

  marker.bindPopup(`
        <b>${destination.name}</b><br>
        ${destination.info}<br>
        <div class="carousel" id="${markerId}_carousel"></div>
    `).on('popupopen', function() {
    initSlickCarousel(markerId, destination.images);
  }).openPopup();
});

// 计算点击点到路由路径的最小距离
function getDistanceToRoute(clickLatLng) {
  let minDistance = Infinity;
  // 遍历路径的每一段线段
  for (let i = 0; i < routeCoordinates.length - 1; i++) {
    const segmentStart = routeCoordinates[i];
    const segmentEnd = routeCoordinates[i + 1];
    // 计算点到线段的距离(单位:米)
    const distance = L.LineUtil.pointToSegmentDistance(clickLatLng, segmentStart, segmentEnd);
    if (distance < minDistance) {
      minDistance = distance;
    }
  }
  return minDistance;
}

// 修改地图点击事件:仅在路线附近才添加街景标记
map.on('click', function(e) {
  // 如果还没获取到路由路径,直接返回
  if (routeCoordinates.length === 0) return;

  const clickPoint = e.latlng;
  const distanceToRoute = getDistanceToRoute(clickPoint);

  // 距离小于阈值才允许添加标记
  if (distanceToRoute <= DISTANCE_THRESHOLD) {
    if (streetViewMarker) {
      map.removeLayer(streetViewMarker);
    }

    let lat = clickPoint.lat.toPrecision(8);
    let lon = clickPoint.lng.toPrecision(8);
    streetViewMarker = L.marker([lat, lon]).addTo(map)
      .bindPopup(`<a href="http://maps.google.com/maps?q=&layer=c&cbll=${lat},${lon}&cbp=11,0,0,0,0" target="blank"><b>点击查看该区域街景!</b></a>`).openPopup();
  }
});

关键改动说明

  1. 新增routeCoordinates变量:用于存储路由生成的完整路径坐标点。
  2. 监听路由route事件:当路由生成完成后,把路径坐标存入routeCoordinates。
  3. getDistanceToRoute函数:遍历路径的每一段线段,用Leaflet内置方法计算点击点到线段的距离,返回最小距离。
  4. 修改点击事件逻辑:先判断点击点到路径的距离是否小于设定的DISTANCE_THRESHOLD(50米),只有满足条件才添加街景标记。

你可以根据实际需求调整DISTANCE_THRESHOLD的值,比如缩小到20米或者扩大到100米,优化用户点击的容错范围。

内容的提问来源于stack exchange,提问作者wanderzen

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.02 09:44:52