Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Leaflet: Including metadata with CircleMarkers

I have a Leaflet map that I am populating with CircleMarkers. I would like to include an additional value (a database ID) with each circle so that when I click on the circle, I can get the value and navigate somewhere else.

I would like to add the value directly to the marker and use a callback function on the entire featureGroup instead of adding a callback function to each marker, since we're dealing with over 500 markers and it would be a performance drag.

Worth mentioning: I'm using Typescript inside an Angular app, but it's still Leaflet.

What I've tried:

  var data = [
    {lat: 20.45, lng: -150.2, id: 44},
    {lat: 23.45, lng: -151.7, id: 45},
  ]
  var points = [];

  data.forEach((d) => {
    // How do I add an additional variable to this circleMarker?
    points.push(circleMarker(latLng(d.lat, d.lng), { radius: 5}));
  })

  var group = featureGroup(points);

  group.on("click", function (e) {
    console.log(e);
    // This is where I would like to get the ID number of the record
  });
like image 861
caitlin Avatar asked Apr 25 '18 00:04

caitlin


2 Answers

FWIW, you have plenty ways of adding your own data to Leaflet Layers (nothing specific to Circle Markers, it is the same for Markers, but also Polygons, Polylines, etc.).

See for instance: Leaflet/Leaflet #5629 (Attach business data to layers)

In short, there are mainly 3 possible ways:

  • Just directly add some properties to the Leaflet Layer after it has been instantiated. Make sure you avoid collision with library properties and methods. You can add your own prefix to the property name to decrease the chance of collision.
var marker = L.marker(latlng);
marker.myLibTitle = 'my title';
  • Use the Layer options (usually the 2nd argument of the instantiation factory), as shown by @nikoshr. As previously, avoid collision with library option names.
L.marker(latlng, {
  myLibTitle: 'my title'
});
  • Use the Layer GeoJSON properties. Leaflet does not use those for internal purpose, so you have total freedom of this data, without any risk of collision.
L.Layer.include({
  getProps: function () {
    var feature = this.feature = this.feature || {}; // Initialize the feature, if missing.
    feature.type = 'Feature';
    feature.properties = feature.properties || {}; // Initialize the properties, if missing.
    return feature.properties;
  }
});

var marker = L.marker(latlng);

// set data
marker.getProps().myData = 'myValue';

// get data
myFeatureGroup.on('click', function (event) {
  var source = event.sourceTarget;
  console.log(source.getProps().myData);
});
like image 123
ghybs Avatar answered Nov 11 '22 05:11

ghybs


  • Events fired on members of a FeatureGroup are propagated to the FeatureGroup object
  • Event objects expose a sourceTarget member giving you access to the source marker
  • Options in a layer can be accessed as marker.options

From there, you could pass your id as a member of the options object when building your markers and retrieve this value when a marker is clicked. For example:

var points = data.map((datum) => {
    return L.circleMarker(datum, {radius: 5, id: datum.id});
});

var group = L.featureGroup(points);
group.addTo(map);

group.on("click", (e) => {
    console.log(e.sourceTarget.options.id);
});

And a demo

var data = [
	{lat: 20.45, lng: -150.2, id: 44},
	{lat: 23.45, lng: -151.7, id: 45},
]
var points = [];

var map = L.map('map', {
    center: [20.45, -150.2],
    zoom: 4
});

var points = data.map(function (datum) {
    return L.circleMarker(datum, {radius: 5, id: datum.id});
});

var group = L.featureGroup(points);
group.addTo(map);

group.on("click", function (e) {
    console.log(e.sourceTarget.options.id);
});
html, body {
  height: 100%;
  margin: 0;
}
#map {
  width: 100%;
  height: 150px;
}
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.3.1/leaflet.css"/>
<script src="https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.3.1/leaflet.js"></script>

<div id='map'></div>
like image 35
nikoshr Avatar answered Nov 11 '22 06:11

nikoshr