【问题标题】:How can I grab a selection of markers with Leaflet.draw?如何使用 Leaflet.draw 抓取一系列标记?
【发布时间】:2017-05-30 11:32:15
【问题描述】:

上下文:

我制作了一张地图,并在其中填充了大约 300 个随机标记。我可以通过单击弹出窗口中的链接来“选择”标记并激活选择以显示数据。我还有 Leaflet.draw 插件来绘制圆形、矩形和自定义形状等形状,我想用它来“选择”几个标记。

问题

如何抓取落入已绘制的 leaflet.draw 形状内的标记的传单标记对象,以便编辑它们?我似乎无法进行选择,它要么不选择任何标记,要么全部选择。

代码 sn-p,从不必要的代码中剥离:

const drawControl = new L.Control.Draw({
    draw: {
        marker   : false,
        polygon  : true,
        polyline : false,
        rectangle: true,
        circle   : {
            metric: 'metric'
        }
    },
    edit: false
});

const map = L.map('map', {
    layers: [streets, light]
}).setView([CONFIG.MAP.LATITUDE, CONFIG.MAP.LONGITUDE], CONFIG.MAP.ZOOMLEVEL)

map.addControl(drawControl);

map.on(L.Draw.Event.DRAWSTOP, e => {

    const hello = e.target;

    console.log(hello);
    e.target.eachLayer(layer => {
        if (layer.options.icon) {
            console.log(layer);
        }
    });

});

【问题讨论】:

    标签: javascript leaflet leaflet.draw


    【解决方案1】:

    使用 Leaflet 的实用方法可以很容易地完成您想要的大部分操作。如果您想使用像 L.Polygon 这样的复杂形状来执行此操作,您将需要像 TurfJS 这样的东西

    对于L.Circle,您需要计算圆心之间的距离并将其与半径进行比较:

    var marker = new L.Marker(...),
        circle = new L.Circle(...);
    
    var contains = circle.getLatLng().distanceTo(marker.getLatLng()) < circle.getRadius();
    

    对于L.Rectangle,您需要获取它的边界对象并使用 contains 方法:

    var marker = new L.Marker(...),
        rectangle = new L.Rectangle(...);
    
    var contains = rectangle.getBounds().contains(marker.getLatLng());
    

    正如对于复杂多边形所说,我使用的是 Turf,但那里有更多的库和插件。这是一个使用 Turf 的 inside 方法的示例。它采用 GeoJSON 点和多边形特征作为参数,因此请注意转换:

    var marker = new L.Marker(...),
        polygon = new L.Polygon(...);
    
    var contains = turf.inside(marker.toGeoJSON(), polygon.toGeoJSON());
    

    您可以将它们包装到每个相应类的便利方法中:

    L.Polygon.include({
        contains: function (latLng) {
            return turf.inside(new L.Marker(latLng).toGeoJSON(), this.toGeoJSON());
        } 
    });
    
    L.Rectangle.include({
        contains: function (latLng) {
            return this.getBounds().contains(latLng);
        }
    });
    
    L.Circle.include({
        contains: function (latLng) {
            return this.getLatLng().distanceTo(latLng) < this.getRadius();
        }
    });
    
    var marker = new L.Marker(...),
        polygon = new L.Polygon(...),
        rectangle = new L.Rectangle(...),
        circle = new L.Circle(...);
    
    polygon.contains(marker.getLatLng());
    rectangle.contains(marker.getLatLng());
    circle.contains(marker.getLatLng());
    

    请注意,如果您实现了多边形方法,则不需要矩形方法。由于矩形是从多边形扩展而来的,它将继承该方法。我把它放在那里是为了完成。

    现在迭代你的标记并比较它们很容易:

    map.on(L.Draw.Event.CREATED, function (e) {
        markers.eachLayer(function (marker) {
            if (!e.layer.contains(marker.getLatLng())) {
                marker.remove();
            }
        });
    });
    

    希望对您有所帮助,这是一个有效的 sn-p:

    var map = new L.Map('leaflet', {
        'center': [0, 0],
        'zoom': 0
    });
    
    var markers = new L.LayerGroup().addTo(map);
    
    for (var i = 0; i < 300; i++) {
        var marker = new L.Marker([
            (Math.random() * (90 - -90) + -90).toFixed(5) * 1,
            (Math.random() * (180 - -180) + -180).toFixed(5) * 1
        ]).addTo(markers);
    }
    
    new L.Control.Draw({
        draw: {
            marker   : false,
            polygon  : true,
            polyline : false,
            rectangle: true,
            circle   : {
                metric: 'metric'
            }
        },
        edit: false
    }).addTo(map);
    
    L.Polygon.include({
        contains: function (latLng) {
            return turf.inside(new L.Marker(latLng).toGeoJSON(), this.toGeoJSON());
        } 
    });
    
    L.Rectangle.include({
        contains: function (latLng) {
            return this.getBounds().contains(latLng);
        }
    });
    
    L.Circle.include({
        contains: function (latLng) {
            return this.getLatLng().distanceTo(latLng) < this.getRadius();
        }
    });
    
    map.on(L.Draw.Event.CREATED, function (e) {
        markers.eachLayer(function (marker) {
            if (!e.layer.contains(marker.getLatLng())) {
                marker.remove();
            }
        });
    });
    body {
        margin: 0;
    }
    
    html, body, #leaflet {
        height: 100%;
    }
    <!DOCTYPE html>
    <html>
      <head>
        <title>Leaflet 1.0.3</title>
        <meta charset="utf-8" />
        <meta name="viewport" content="width=device-width, initial-scale=1.0" />
        <link type="text/css" rel="stylesheet" href="//unpkg.com/leaflet@1.0.3/dist/leaflet.css" />
        <link type="text/css" rel="stylesheet" href="//cdnjs.cloudflare.com/ajax/libs/leaflet.draw/0.4.9/leaflet.draw.css" />
      </head>
      <body>
        <div id="leaflet"></div>
        <script type="application/javascript" src="//unpkg.com/leaflet@1.0.3/dist/leaflet.js"></script>
        <script type="application/javascript" src="//cdnjs.cloudflare.com/ajax/libs/leaflet.draw/0.4.9/leaflet.draw.js"></script>
        <script type="application/javascript" src="//unpkg.com/@turf/turf@latest/turf.min.js"></script>
      </body>
    </html>

    【讨论】:

    • 天哪,我整个上午都在看这个,让我试着把它转换成我自己的代码库,非常感谢你到目前为止的帮助!
    • 效果很好,非常非常感谢您的帮助:)。
    • 绝对没问题,无需感谢,随时欢迎。这就是我们来这里的目的 ;)
    【解决方案2】:

    感谢@iH8 提供了很酷的示例。我进一步避免了一些重复

    markers.eachLayer(function (marker) {
        ...
    }
    

    并使用标记数组来扩展包装器:


    首先我注意到LayerGroup 有一个对象,其键值对包含所有标记。我只是使用该对象来创建一个标记数组:

    // In the on draw event
    ...
    // Set an array containing all the markers
    var markers = jsonToArray(layerGroup._layers); 
    ...
    
    function jsonToArray(jsonObject) {
      var result = [];
      var keys = Object.keys(jsonObject);
      keys.forEach(function (key) {
        result.push(jsonObject[key]);
      });
      return result;
    }
    

    然后我通过修改的contains() 方法重新使用包装器:

      L.Rectangle.include({
        // Single marker case
        contains: function (marker) {
          return this.getBounds().contains(marker.getLatLng());
        },
        // Array of markers
        contains: function (markers) {
          var markersContained = [];
          markers.forEach(marker => {
            markersContained.push(this.getBounds().contains(marker.getLatLng()));
          })
          return markersContained;
        }
      });
    
      L.Circle.include({
        contains: function (marker) {
          return this.getLatLng().distanceTo(marker.getLatLng()) < this.getRadius();
        },
        contains: function (markers) {
          var markersContained = [];
          markers.forEach(marker => {
            markersContained.push(this.getLatLng().distanceTo(marker.getLatLng()) < this.getRadius());
          })
          return markersContained;
        }
      });
    

    最后在抽奖事件中,我检查我的标记是否包含在其中:

      map.on(L.Draw.Event.CREATED, function (geometry) {
        // Set an array containing all the markers
        var markers = jsonToArray(layerGroup._layers);
    
        var result = geometry.layer.contains(markers);
        console.log('result => ', result);
      });
    

    function jsonToArray(jsonObject) {
      var result = [];
      var keys = Object.keys(jsonObject);
      keys.forEach(function (key) {
        result.push(jsonObject[key]);
      });
      return result;
    }
    
    var map = new L.Map('leaflet', {
        'center': [0, 0],
        'zoom': 0
    });
    
    var layerGroup = new L.LayerGroup().addTo(map);
    
    for (var i = 0; i < 10; i++) {
        var marker = new L.Marker([
            (Math.random() * (90 - -90) + -90).toFixed(5) * 1,
            (Math.random() * (180 - -180) + -180).toFixed(5) * 1
        ]).addTo(layerGroup);
    }
    
    new L.Control.Draw({
        draw: {
            marker   : false,
            polygon  : false,
            polyline : false,
            rectangle: true,
            circle   : {
                metric: 'metric'
            }
        },
        edit: false
    }).addTo(map);
    
    // Define contains() method for each geometry
    L.Rectangle.include({
      contains: function (marker) {
        return this.getBounds().contains(marker.getLatLng());
      },
      contains: function (markers) {
        var markersContained = [];
        markers.forEach(marker => {
          markersContained.push(this.getBounds().contains(marker.getLatLng()));
        })
        return markersContained;
      }
    });
    
    L.Circle.include({
      contains: function (marker) {
        return this.getLatLng().distanceTo(marker.getLatLng()) < this.getRadius();
      },
      contains: function (markers) {
        var markersContained = [];
        markers.forEach(marker => {
          markersContained.push(this.getLatLng().distanceTo(marker.getLatLng()) < this.getRadius());
        })
        return markersContained;
      }
    });
    
    map.on(L.Draw.Event.CREATED, function (geometry) {
      // Set an array containing all the markers
      var markers = jsonToArray(layerGroup._layers);
    
      var result = geometry.layer.contains(markers);
      console.log('result => ', result);
    });
    body {
        margin: 0;
    }
    
    html, body, #leaflet {
        height: 100%;
    }
    <!DOCTYPE html>
    <html>
      <head>
        <title>Leaflet 1.0.3</title>
        <meta charset="utf-8" />
        <meta name="viewport" content="width=device-width, initial-scale=1.0" />
        <link type="text/css" rel="stylesheet" href="//unpkg.com/leaflet@1.0.3/dist/leaflet.css" />
        <link type="text/css" rel="stylesheet" href="//cdnjs.cloudflare.com/ajax/libs/leaflet.draw/0.4.9/leaflet.draw.css" />
      </head>
      <body>
        <div id="leaflet"></div>
        <script type="application/javascript" src="//unpkg.com/leaflet@1.0.3/dist/leaflet.js"></script>
        <script type="application/javascript" src="//cdnjs.cloudflare.com/ajax/libs/leaflet.draw/0.4.9/leaflet.draw.js"></script>
      </body>
    </html>

    【讨论】:

    • 这非常有帮助,谢谢!你能给我建议一种方法来控制台记录标记内容而不是真/假吗?理想情况下,我想在地图上选择一组图钉并返回某种图钉 ID。
    • 我已经很久没有从事这个项目了,但我想你可以尝试记录标记本身吗?老实说,我真的不知道了
    • 我最终整理了这个,以防它对我在这里包含我的代码的任何人有帮助。这将返回标记数据而不是 True 和 False : L.Rectangle.include({ contains: function (markers) { var markersContained = markers.filter(marker => { return this.getBounds().contains(marker.getLatLng() ) === true; }) 返回标记包含; } });
    【解决方案3】:

    我用过:

    L.Circle.include({
        contains: function (latLng) {
            return this.getLatLng().distanceTo(latLng) < this.getRadius();
        }
    });
    

    在边缘但不在圆内的点也会被判断。

    【讨论】:

      猜你喜欢
      • 2012-10-31
      • 1970-01-01
      • 2020-11-12
      • 1970-01-01
      • 1970-01-01
      • 2018-05-25
      • 2013-07-20
      • 1970-01-01
      • 2018-06-03
      相关资源
      最近更新 更多