Как я могу получить набор маркеров с помощью Leaflet.draw?

Контекст:

Я сделал карту и заселил ее примерно 300 случайными маркерами. Я могу «выбрать» маркеры, щелкнув ссылку во всплывающем окне, и активировать выбор для отображения данных. У меня также есть плагин Leaflet.draw для рисования фигур, таких как круги, прямоугольники и произвольные фигуры, и я хотел бы использовать его для «выбора» пары маркеров.

Проблема

Как я могу захватить объект-маркер-листок из маркеров, которые попадают внутрь нарисованной фигуры leaflet.draw, чтобы я мог их редактировать? Кажется, я не могу сделать выбор, он либо не выбирает ни один из маркеров, либо все они.

Фрагмент кода, лишенный ненужного кода:

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);
        }
    });

});

person roberrrt-s    schedule 30.05.2017    source источник


Ответы (3)


Большую часть того, что вы хотите, довольно легко сделать с помощью служебных методов 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, но есть и другие библиотеки и плагины. Вот пример использования метода Турфа 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();
        }
    });
});

Надеюсь, что это поможет, вот рабочий фрагмент:

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/[email protected]/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/[email protected]/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>

person iH8    schedule 30.05.2017
comment
Святые бананы, я просматривал это все утро, позвольте мне попробовать преобразовать его в мою собственную кодовую базу, огромное спасибо за вашу помощь! - person roberrrt-s; 31.05.2017
comment
Работает фантастически, спасибо большое за помощь :). - person roberrrt-s; 31.05.2017
comment
Абсолютно никаких проблем, не надо благодарить, всегда рады, вот для чего мы здесь;) - person iH8; 31.05.2017

Спасибо @ 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/[email protected]/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/[email protected]/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>

person Alex Beugnet    schedule 07.09.2017
comment
Это было очень полезно, спасибо! Можете ли вы предложить мне способ console.log для содержимого маркера, а не true / false? В идеале я хотел бы выбрать группу контактов на карте и вернуть какой-то идентификатор контакта. - person Josh; 17.07.2019
comment
Я давно не работал над этим проектом, но, думаю, вы могли бы попробовать записать сам маркер? Честно говоря, я больше не знаю - person Alex Beugnet; 23.07.2019
comment
В итоге я разобрался с этим, на случай, если это будет полезно для кого-то, я включил сюда свой код. Это возвращает данные маркера, а не True и False: L.Rectangle.include ({contains: function (markers) {var markersConhibited = markers.filter (marker = ›{return this.getBounds (). Contains (marker.getLatLng ()) ) === true;}) return markersConhibited;}}); - person Josh; 24.07.2019

Я использовал это :

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

Очки, которые находятся на краю, но не в круге, также оцениваются.

person Husir    schedule 25.09.2018