2

I am using a permanent tooltip on each marker but the problem is that tooltip texts are overlapping each other and markers are updating every 10 seconds interval. I can't use LayerGroup collision because I need to show markers. I can't find any solution to solve this issue. I tried labelgun but no luck. I used BingMap before in my project and BingMap manages this overlapping issue internally.

Here is my sample code

   
   let marker = L.marker(pos, {options});
   marker.bindTooltip(vehicleName, {
                       direction: 'auto',
                       permanent: true,
                       className: "my-labels",
                       offset: L.point(5,15)
   });
   
   newLayer.addLayer(marker);
   newLayer.addTo(map);

enter image description here

See below image how BingMap shows label enter image description here

6
  • Coding question on GIS SE site should include relevant existing code, otherwise it's most likely to be closed as not compliant with the site policy. Please edit your question and add relevant existing code. – TomazicM Dec 15 '20 at 16:06
  • Can you also add some visual example of how it was successfully managed in BingMap. – TomazicM Dec 15 '20 at 16:10
  • @TomazicM edited my question – Sikder Dec 15 '20 at 19:46
  • But there are tens and tens of buses, but only six labels. If you are satisfied with that, you can use Leaflet.LabelTextCollision plugin: github.com/yakitoritabetai/Leaflet.LabelTextCollision. – TomazicM Dec 15 '20 at 19:51
  • @TomazicM Does it support L.marker? – Sikder Dec 15 '20 at 19:55
2

This will be brute force simple solution for the case when tooltip number is not too high. It's based on the fact it's possible to get all HTML elements with specific CSS class name with the call document.getElementsByClassName. This can be used to get all tooltip elements, if we assign them some dummy CSS class with the className option.

Logic of the solution goes like this (unoptimized brute force):

  • get all tooltip HTML elements with the document.getElementsByClassName call;
  • get bounding rects of all the elements with the .getBoundingClientRect() method;
  • set all elements visible;
  • iterate through all the elements and hide overlapping ones;
  • this process is done after each map zoom.

Example below is based on creating 250 random polygons with permanent tooltips with the help of turf.js library (here is working JSFiddle: https://jsfiddle.net/TomazicM/L2kewosz/).

function overlap(rect1, rect2) {
  return(!(rect1.right < rect2.left || 
          rect1.left > rect2.right || 
          rect1.bottom < rect2.top || 
          rect1.top > rect2.bottom));
}

function hideOverlappingTooltips() {
  var rects = [];
  var tooltips = document.getElementsByClassName('myTooltip');
  for (var i = 0; i < tooltips.length; i++) {
    tooltips[i].style.visibility = '';
    rects[i] = tooltips[i].getBoundingClientRect();
  }
  for (var i = 0; i < tooltips.length; i++) {
    if (tooltips[i].style.visibility != 'hidden') {
      for (var j = i + 1; j < tooltips.length; j++) {
        if (overlap(rects[i], rects[j])) tooltips[j].style.visibility = 'hidden';
      }
    }
  }
}

var map = L.map('map');

L.tileLayer('http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
  maxZoom: 18,
  attribution: '&copy; <a href="http://openstreetmap.org/copyright">OpenStreetMap</a> contributors'
}).addTo(map);

function onEachFeature(feature, layer) {
  layer.bindTooltip(feature.id.toString(), {
    permanent: true,
    className: 'myTooltip'
  });
}

polygons = turf.randomPolygon(250, {bbox: [13.24, 45.5, 16.35, 46.84], max_radial_length: 0.01 });
turf.featureEach(polygons, function (currentFeature, featureIndex) {
  currentFeature.id = featureIndex;
});
var geojson = L.geoJson(polygons, {
  onEachFeature: onEachFeature
}).addTo(map);

map.setView(geojson.getBounds().getCenter(), 10);

hideOverlappingTooltips();

map.on('zoomend', function(evt) {
  hideOverlappingTooltips();
});
1
  • TomazicM, It works. Thank you so much for your help – Sikder Dec 18 '20 at 20:18
2

Leaflet map object supports different events. In your case you need mouseover and mouseout - one for showing tooltips and one for killing it.

After adding vector layer to the map, call two mentioned events:

vectorLayer.on('mouseover', this._onMapHoverIn);
vectorLayer.on('mouseout', this._onMapHoverOut);

Functions _onMapHoverIn and _onMapHoverOut aim to handle tooltip when mouse is located on feature and leaves it.

_onMapHoverIn = (e) => {
   mapObject.eachLayer(layer => {
      if (layer === e.layer) {
         layer.bindTooltip('', {
           sticky: true,
           direction: 'top',
           offset: L.point([0, -10])
         })
         layer.setTooltipContent(`ID: ${e.layer.feature.properties.id}`)
         layer.openTooltip();
         return true;
      }
   })
}
_onMapHoverOut = (e) => {
   mapObject.eachLayer(layer => {
      if (layer === e.layer) {
         layer.unbindTooltip();
         layer.closeTooltip();
         return true;
      }
   })
}
1
  • This does not answer the question, since the question is about overlapping permanent tooltips. – TomazicM Dec 18 '20 at 8:40

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.