从CircleMarker扩展的标记-如何更改点击区域? [英] Markers extended from CircleMarker - How to change the click area?

查看:110
本文介绍了从CircleMarker扩展的标记-如何更改点击区域?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

使用Leaflet,我试图将很多点(+ 10000)渲染为GeoJSON功能,以提高性能.为了获得更好的结果,我找到了

然后在 L.GeoJSON pointToLayer 选项中使用

MarkerPin ,如下所示:

  const myPointToLayer =(feature,latlng)=>{var markerParams = {半径:16笔画:是的,重量:2不透明度:0.4,fillOpacity:0.9,}返回新的MarkerPin(latlng,markerParams)}const myOnEachFeature =(功能,层)=>{layer.bindPopup('点击我!')}L.geoJSON(data,{pointToLayer:myPointToLayer,onEachFeature:myOnEachFeature,}).addTo(map); 

到目前为止,一切都很好.所有数据都会正确渲染.我的问题是:点击区域没有根据我的新形状进行更新,它仍然像红色圆圈所示的 circleMarker 一样(因为这就是我为创建图钉而进行的扩展):

其他Leaflet元素(例如多边形)的点击区域与形状匹配.话虽如此,是否可以更改我的 MarkerPin 的点击区域以匹配我的形状(即使我正在扩展 circleMarker )?

即使无法更改点击区域的形状,我也很满意能够将其向上移动,如下图所示:

甚至,是否有更好的方法来用自定义图标在GeoJSON中呈现多个标记,并使用Leaflet处理其弹出窗口?

修改

出于完整性考虑,这就是我正在使用的(感谢 https://stackoverflow.com/a/56072877/11064013 ),它使用整个引脚区域:

  const MarkerPin = L.CircleMarker.extend({_updatePath:function(){this._renderer._updateMarkerPin(this)},_containsPoint:函数(p){令r = this._radius让insideCircle =p.add([0,r * 2]).distanceTo(this._point)< = r + this._clickTolerance()让 a = this._point,b = a.subtract([0.58 * r,r]),c = a.subtract([-0.58 * r,r])让insideTriangle = true让 ap_x = p.x - a.x令ap_y = p.y-a.y令p_ab =(b.x-a.x)* ap_y-(b.y-a.y)* ap_x>0令p_ac =(c.x-a.x)* ap_y-(c.y-a.y)* ap_x>0令p_bc =(c.x-b.x)*(p.y-b.y)-(c.y-b.y)*(p.x-b.x)>0如果(p_ac === p_ab){insideTriangle = false}如果(p_bc!== p_ab){insideTriangle = false}返回insideTriangle ||insideCircle},}) 

解决方案

在通过 L.Canvas 渲染的矢量层中的单击检测取决于每种方法的 _containsPoint 私有方法向量层.请参阅 CircleMarker.js#L94 上的实施标记代码> 折线 .

每次用户点击 L.Canvas 渲染器,渲染器循环遍历其中的所有图层,并要求他们用户已完成像素 p 中的某件事-这是您的吗"?调用 _containsPoint 必须对此进行回答.

因此,您必须尝试为 MarkerPin 类实现 _containsPoint ,例如

  const MarkerPin = L.CircleMarker.extend({_updatePath:function(){this._renderer._updateMarkerPin(this)},_containsPoint:函数(p){返回L.CircleMarker.prototype._containsPoint.call(this,p.subtract([0,10]));}}) 

这应该将点击区域上移或下移.如果想要更好的形状,则必须提供函数的正确实现,该函数将返回是否在形状内部的点.

With Leaflet, I'm trying to render a lot of points (+ 10000) as a GeoJSON feature to improve performance. In order to achieve better results, I found this answer instructing on how to extend Leaflet's circleMarker to change it's shape like so:

L.Canvas.include({
    _updateMarkerPin: function(layer) {
        if (!this._drawing || layer._empty()) {
            return
        }

        var p = layer._point,
            ctx = this._ctx,
            r = layer._radius

        this._drawnLayers[layer._leaflet_id] = layer

        ctx.beginPath()
        ctx.moveTo(p.x, p.y)
        ctx.lineTo(p.x - 0.58 * r, p.y - r)
        ctx.arc(p.x, p.y - 2 * r, r, -Math.PI * 1.161, Math.PI * 0.161)
        ctx.closePath()
        this._fillStroke(ctx, layer)
    },
})

const MarkerPin = L.CircleMarker.extend({
    _updatePath: function() {
        this._renderer._updateMarkerPin(this)
    },
})

This is my MarkerPin Shape:

MarkerPin is then used in L.GeoJSON's pointToLayer option as follows:

const myPointToLayer = (feature, latlng) => {
    var markerParams = {
        radius: 16,
        stroke: true,
        weight: 2,
        opacity: 0.4,
        fillOpacity: 0.9,
    }
    return new MarkerPin(latlng, markerParams)
}

const myOnEachFeature = (feature, layer) => {
    layer.bindPopup('Clicked me!')
}

L.geoJSON(data, {
    pointToLayer:myPointToLayer,
    onEachFeature: myOnEachFeature,
}).addTo(map);

So far so good. All the data gets rendered properly. My problem is: the click area isn't updated according to my new shape, it stays like the circleMarker's one (because that's what I've extended to create my pin) illustrated by the red circle:

Other Leaflet elements, like polygons, have their click area match their shape. That being said, is it possible to change the click area of my MarkerPin to match my shape (even if I'm extending circleMarker)?

Even if it's not possible to change the shape of the click area, I'd be satisfied to being able to move it up a bit like the image below:

Or even, is there a better approach to render several markers in GeoJSON with a custom icon and handle their popups with Leaflet?

Edit

For completeness, this is what I'm using (thanks to https://stackoverflow.com/a/56072877/11064013), which uses the whole pin area:

const MarkerPin = L.CircleMarker.extend({
    _updatePath: function() {
        this._renderer._updateMarkerPin(this)
    },
    _containsPoint: function(p) {
        let r = this._radius

        let insideCircle =
            p.add([0, r * 2]).distanceTo(this._point) <= r + this._clickTolerance()

        let a = this._point,
            b = a.subtract([0.58 * r, r]),
            c = a.subtract([-0.58 * r, r])

        let insideTriangle = true

        let ap_x = p.x - a.x
        let ap_y = p.y - a.y

        let p_ab = (b.x - a.x) * ap_y - (b.y - a.y) * ap_x > 0
        let p_ac = (c.x - a.x) * ap_y - (c.y - a.y) * ap_x > 0
        let p_bc = (c.x - b.x) * (p.y - b.y) - (c.y - b.y) * (p.x - b.x) > 0

        if (p_ac === p_ab) {
            insideTriangle = false
        }
        if (p_bc !== p_ab) {
            insideTriangle = false
        }
        return insideTriangle || insideCircle
    },
})

解决方案

Click detection in vector layers rendered through a L.Canvas depends on the _containsPoint private method of each vector layers. See the implementation on CircleMarker or on Polyline.

Each time the user clicks on a L.Canvas renderer, the renderer loops through all the layers in it, and asks them "the user has done something in pixel p - is this yours"? The call to _containsPoint has to answer that.

So you'll have to play around with implementing _containsPoint for your MarkerPin class, maybe something like e.g.

const MarkerPin = L.CircleMarker.extend({
    _updatePath: function() {
        this._renderer._updateMarkerPin(this)
    },
    _containsPoint: function(p) {
        return L.CircleMarker.prototype._containsPoint.call(this, p.subtract([0, 10]));
    }
})

That should shift the click area up or down. If you want a better shape, you'll have to provide a proper implementation of a function that returns whether a point in inside the shape or not.

这篇关于从CircleMarker扩展的标记-如何更改点击区域?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆