How to use D3 graphic inside popup mapbox?

Viewed 307

I am trying to display a D3 graphic inside a popup of mapbox. I am not managing to make it because popup of mapbox is just accepting plain HTML. Is there a way to make it? Like in the following pic where the graphic is inside the popup.

enter image description here

2 Answers

The way I was able to make it is using Chart.min.js. and jQuery. enter image description here

Here is a fiddle I have created to show you how to create a popup with a chart.

Relevant code is below. First of all add your links to jquery and chart.js

<!-- jQuery -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
<!-- ChartJS -->
<script src="https://cdn.jsdelivr.net/npm/chart.js@2.9.4/dist/Chart.min.js"></script>

Then you add a canvas that will be managed by chart.js

        var popup = new mapboxgl.Popup({ closeOnClick: false })
            .setLngLat([-96, 37.8])
            .setHTML('<div class="chart"><canvas id="lineChart" style="min-height: 250px; height: 250px; max-height: 250px; max-width: 100%;"></canvas></div>')
            .addTo(map);

and then add the data sources, options and render the chart in the canvas

   //this is fake data just for testing
   var areaChartData = {
      labels  : ['January', 'February', 'March', 'April', 'May', 'June', 'July'],
      datasets: [
        {
          label               : 'Digital Goods',
          backgroundColor     : 'rgba(60,141,188,0.9)',
          borderColor         : 'rgba(60,141,188,0.8)',
          pointRadius          : false,
          pointColor          : '#3b8bba',
          pointStrokeColor    : 'rgba(60,141,188,1)',
          pointHighlightFill  : '#fff',
          pointHighlightStroke: 'rgba(60,141,188,1)',
          data                : [28, 48, 40, 19, 86, 27, 90]
        },
        {
          label               : 'Electronics',
          backgroundColor     : 'rgba(210, 214, 222, 1)',
          borderColor         : 'rgba(210, 214, 222, 1)',
          pointRadius         : false,
          pointColor          : 'rgba(210, 214, 222, 1)',
          pointStrokeColor    : '#c1c7d1',
          pointHighlightFill  : '#fff',
          pointHighlightStroke: 'rgba(220,220,220,1)',
          data                : [65, 59, 80, 81, 56, 55, 40]
        },
      ]
    }

    //these are the options for testing
    var areaChartOptions = {
      maintainAspectRatio : false,
      responsive : true,
      legend: {
        display: false
      },
      scales: {
        xAxes: [{
          gridLines : {
            display : false,
          }
        }],
        yAxes: [{
          gridLines : {
            display : false,
          }
        }]
      }
    }


    var lineChartCanvas = $('#lineChart').get(0).getContext('2d')
    var lineChartOptions = jQuery.extend(true, {}, areaChartOptions)
    var lineChartData = jQuery.extend(true, {}, areaChartData)
    lineChartData.datasets[0].fill = false;
    lineChartData.datasets[1].fill = false;
    lineChartOptions.datasetFill = false

    var lineChart = new Chart(lineChartCanvas, { 
      type: 'line',
      data: lineChartData, 
      options: lineChartOptions
    })

If this answer solves your issue, please mark it as answer accepted, in that way other users will know it was the right solution.

A Mapbox popup has a getElement() method that you can use to get the DOM element for the popup.

You can then use getElementsByClassName() on the DOM element to get an element in your popup where you want to render the D3 graphic:

const map = new mapboxgl.Map(...);

const popup = new mapboxgl.Popup()
  .setLngLat([-96, 37.8])
  .setHTML('<svg class="popup-d3-svg" style="margin: 0 auto; display: block;"></svg>')
  .addTo(map);
  
const svgElement = popup.getElement().getElementsByClassName('popup-d3-svg')[0];

const svg = d3.select(svgElement)
  .attr('width', 100)
  .attr('height', 100)
  .style('background-color', 'black');

svg.append('line')
  .style('stroke', 'lightgreen')
  .style('stroke-width', 2)
  .attr('x1', 0)
  .attr('y1', 0)
  .attr('x2', 50)
  .attr('y2', 50);

Here is a working example using this technique: https://jsfiddle.net/Ljtx58pv/2/

Related