如何限制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(); } });
关键改动说明
- 新增
routeCoordinates变量:用于存储路由生成的完整路径坐标点。 - 监听路由
route事件:当路由生成完成后,把路径坐标存入routeCoordinates。 getDistanceToRoute函数:遍历路径的每一段线段,用Leaflet内置方法计算点击点到线段的距离,返回最小距离。- 修改点击事件逻辑:先判断点击点到路径的距离是否小于设定的
DISTANCE_THRESHOLD(50米),只有满足条件才添加街景标记。
你可以根据实际需求调整DISTANCE_THRESHOLD的值,比如缩小到20米或者扩大到100米,优化用户点击的容错范围。
内容的提问来源于stack exchange,提问作者wanderzen
相关产品推荐
相关产品推荐

