Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Show tooltip on the D3 map

I've created a basic map using D3 with countries geojson. Here is the demo.

Now when the user clicks on any coordinate on the map, I am showing a weather info within a tooltip with weather icon as a marker.

countries = countriesGroup
    .selectAll("path")
    .data(json.features)
    .enter()
    .append("path")
    .attr("d", path)
    .attr("id", function(d, i) {
        return "country" + d.properties.iso_a3;
    })
    .attr("class", "country")
    // add a mouseover action to show name label for feature/country
    .on("mouseover", function(d, i) {
        d3.select("#countryLabel" + d.properties.iso_a3).style("display", "block");
    })
    .on("mouseout", function(d, i) {
        d3.select("#countryLabel" + d.properties.iso_a3).style("display", "none");
    })
    // add an onclick action to zoom into clicked country
    .on("click", function(d, i) {
        var eventLocation = d3.mouse(this);
        var coordinates = projection.invert(eventLocation);
        var proxy = "https://cors-anywhere.herokuapp.com/";
        var wetherInfoUrl =
            "https://api.darksky.net/forecast/c68e9aaf0d467528b9363e383bde6254/" + coordinates[1] + "," + coordinates[0] + "?exclude=minutely,hourly,daily";
        $.ajax({
            url: proxy + wetherInfoUrl,
            success: function(response) {
                tooltipDiv
                    .transition()
                    .duration(200)
                    .style("opacity", 0.9);
                tooltipDiv
                    .html('<h3>Dynamic Weather info: '+ response.currently.humidity +'</h3><br/><img src="https://darksky.net/images/weather-icons/' + response.currently.icon + '.png" alt="clear-night Icon" width="50" height="50">')
                    .style("left", (eventLocation[0] - 250) + "px")
                    .style("top", (eventLocation[1] - 100) + "px");
            }
        });
        d3.selectAll(".country").classed("country-on", false);
        d3.select(this).classed("country-on", true);
        boxZoom(path.bounds(d), path.centroid(d), 20);
    });

Now the issue is, the tooltip remains at the absolute position relative to the body (It stays at the exact position on the screen which is misleading if we pan and zoom the map) whereas I want the tooltip to be relative to the coordinate clicked on the map and should be fixed to the clicked coordinate no matter where we zoom or pan in the map, similar to this leaflet example (In short I want tooltip to behave similarly to the typical map pin markers).

like image 852
Saurabh Palatkar Avatar asked Aug 28 '18 05:08

Saurabh Palatkar


People also ask

What are tooltips in D3 JS?

Tooltips are a feature designers can use when they want to gradually reveal information to users as they hover or place keyboard focus over an element. In this guide, you will learn two approaches to enhancing your D3.js charts by implementing tooltips.

How do I create a tooltip in react D3?

In Using D3.js Inside a React App, the <BarChart /> component creates the <rect> element per sales year. To add a simple tooltip, you can insert the <title /> tag inside the interactable elements. This means that the SVG hierarchy looks like this: The SVG element has been simplified for brevity.

How to display part of a D3 visualization when mousing over it?

Easily the most basic method for displaying data that is part of a d3.js visualization when mousing over part of the graphic is to use the title tag.

How do I show/hide a tooltip in a bar chart?

The solution is effectively to create a tooltip div within our code, then attach the appropriate mouseover, mousemove, and mouseout event functions to each of our bar chart divs to appropriately show/hide our custom tooltip div. When the tooltip is shown, we can easily grab the data we want to actually display as the text.


1 Answers

I was able to do this using CSS and the following snippets:

var tooltipDiv = d3.select("body").append("div")    
    .attr("class", "tooltip")               
    .style("opacity", 0);

div.tooltip {   
    position: absolute;         
    text-align: center;         
    width: 80px;                    
    height: 28px;                   
    padding: 2px;               
    font: 12px sans-serif;      
    background: lightsteelblue; 
    border: 0px;        
    border-radius: 8px;         
    pointer-events: none;           
}

... and then in the svgs,

    .on("mouseover", function(d) {      
        tooltipDiv.transition()     
            .duration(200)      
            .style("opacity", .9);      
        tooltipDiv.html(d.vessel_name)  
            .style("left", (d3.event.pageX) + "px")     
            .style("top", (d3.event.pageY - 28) + "px");    
        })                  
    .on("mouseout", function(d) {       
        div.transition()        
            .duration(500)      
            .style("opacity", 0);   
    })

Definitely an imperfect solution, but hopefully it helps. Full source:

Demo:

http://Ares513.github.io/04-MapsAndViews/

Source:

https://github.com/Ares513/MapsAndViews

like image 107
Athena Avatar answered Nov 04 '22 01:11

Athena