Leaflet - 用于重叠折线的工具提示

时间:2017-02-15 14:00:32

标签: leaflet tooltip polyline overlapping

背景
我正在开发一个基于Web的徒步旅行地图应用程序。因此,基于传单的地图提供了标记的远足径的路线。由于任何远足径都可以是多条路线的一部分,因此路线 - 分别代表路线的相应折线 - 可以重叠。

问题:
每个路径都有其工具提示(由鼠标悬停触发,{sticky:true}),显示其标签对于非重叠折线有效,但只要两条或更多条路线仅重叠折线"在顶部"得到它的工具提示打开。这种行为本身并不坏,但由于所有路线都同样重要,我想在指针的位置显示路线的所有标签(或者最多5个标签+ x更多)。我无法找到与此主题相关的任何问题。

我尝试了什么:
- 为所有路径创建要素组,将工具提示绑定到组,希望工具提示功能提供跨越指针位置的所有折线的数组。事实证明,我只能获得顶部折线的信息 - 我在地图上尝试使用mousemove事件,但没有成功 - 将指针的layerPoint坐标与所有路线进行比较' _rings& _parts layPoint数组用于查找匹配的layerPoints,但成功率仅为5%左右,因为这些layerPoints仅覆盖折线的实际点,但不包括两点之间的连接。此外,每条折线周围都有一个边距,在指针触及折线之前会触发收费(我猜是太改进了触摸动作)
- 边缘问题的解决方案是在将每个折线点与指针坐标进行比较之前为每个折线点添加正边距和负边距,这会改善结果,但不能解决主要问题。

旁注:
- 所有路线都绘制在一个画布中

长话短说,我需要外部帮助来完成目标。也许你们中的一些人有想法或者可以提供解决方案。任何意见都表示赞赏。

** 更新: **
一个工作但非常低效的解决方案如下

方法
计算从指针到视口中所有路径的最短距离。如果指针到路径的距离低于某个阈值,请将它们添加到应显示的路径标签数组中。

步骤:
1.)将空白工具提示绑定到包含所有路径的特征组 2.)使用以下函数将mousemove事件绑定到要素组

var routesFeatureGroup = L.featureGroup(routesGroup)
    .bindTooltip('', {sticky: true})
    .on('mousemove', function(e){
        var routeLabels   = [e.layer.options.label]; // add triggering route's label by default
        var mouseCoordAbs = el.$map.project(e.latlng);

        $.each(vars.objectsInViewport.routes, function(i, v){
            if (e.layer.options.id != el.$routes[i].options.id && el.$routes[i]._pxBounds.contains(e.layerPoint)){
                var nearestLatlngOnPolyline = getNearestPolylinePoint(e.latlng, el.$routes[i]);
                var polyPointCoordAbs = el.$map.project(nearestLatlngOnPolyline);

                var distToMouseX = polyPointCoordAbs.x - mouseCoordAbs.x;
                var distToMouseY = polyPointCoordAbs.y - mouseCoordAbs.y;
                var distToMouse  = Math.sqrt(distToMouseX*distToMouseX + distToMouseY*distToMouseY);

                if (distToMouse < 15) {
                    routeLabels.push(el.$routes[i].options.label);
                }
             }
        })

        var routesFeatureGroup.setTooltipContent(routeLabels.join('<br>'));
    })

解释
我已经在当前视口中收集了应用程序另一部分的所有对象(路径和标记)。当前可见的所有路径都存储在vars.objectsInViewport.routes(分别是它们的ID)中,因此我不必遍历所有路由。默认情况下会添加触发mousemove事件的图层。然后,如果出现以下情况,我会检查当前可见的每条路线:
- 他们的id与触发mousemove事件的图层不同(默认添加此标签) - 如果它们的边界(在笛卡尔坐标中:&#34; _pxBounds&#34;)包含mousemove事件的笛卡尔图层点(对于粗略的approch来排除不相交的路径)

如果路线满足这些条件,请计算指向路线的最近的latlng点。我使用自定义函数执行此操作,在此上下文中发布它有点长。 (如果有人要求,我会的。)

然后使用map-project方法将折线/路线上的鼠标位置和latlng点转换为绝对坐标 http://leafletjs.com/reference.html#map-project

最后,使用毕达哥拉斯计算这些点之间的距离。它是基于像素的,因此缩放级别不是一个因素。如果距离低于某个阈值(15px),则它们足够接近指针被视为悬停(使用折线周围的默认边距),因此路径的标签将添加到标签数组中。

最后,功能组的工具提示将填充所有标签。

即使操作相当昂贵,结果也很有希望。我添加了50ms的超时来减少函数调用:

var tooltipTimeout;
var routesFeatureGroup = L.featureGroup(routesGroup)
    .bindTooltip('', {sticky: true})
    .on('mousemove', function(e){
        clearTimeout(tooltipTimeout);

        tooltipTimeout = setTimeout(function(){
            // collect labels
            // ...
        },50);
    .on('mouseout', function(){
        clearTimeout(tooltipTimeout);
    })

1 个答案:

答案 0 :(得分:0)

我可以告诉你如何做到这一点,但我并不是百分之百确定它会完成这项工作。有一个plugin for Leaflet(Mapbox)可以告诉您一个点是否在多边形内,它会返回包含该点的所有多边形。

如果此插件不适用于折线,您可以从折线创建一个多边形,只需从最后一个点返回到第一个点并关闭该线(我不确定这是否适合您的解决方案)。例如,如果连续点的折线为[0,1,2,.... n-1,n],则返回连接[n与n-1,n-1与n-2 ,. .. 1与0]。这样,您将具有相同的折线形状,但它将是多边形。这不是最优化的解决方案,它是一个使用已知可用插件的快速修复程序。

获得所有工具提示后,您可以为每个多边形/折线一次打开所有工具提示。或者也许打开一些帮助工具提示,用户可以选择要打开哪一个。

我希望这有帮助!如果你找到一个更好的解决方案(或找到一个能完成这项工作的插件),请在此处发布。