0

I'm having difficulty centering my D3 map on the webpage. The map is in a map class and within a div, but CSS won't appear to center it.

Any margins I apply to the class ".map", which the SVG is in, doesn't appear to work either. I'm not sure why I'm unable to apply any CSS to the map, but maybe I'm supposed to do something in the actual D3 code? Unsure. Thanks!

Here's my code:

<!DOCTYPE html>
<html>

<head>
  <script src="https://d3js.org/d3.v4.min.js"></script>
  <script src='https://cdnjs.cloudflare.com/ajax/libs/d3-tip/0.7.1/d3-tip.js'></script>
  <meta charset='utf-8'>
  <meta name="viewport" content='width=device-width, initial-scale=1.0'>
  <title></title>
  <style>
    .d3-tip {
      line-height: 1;
      font-weight: bold;
      padding: 10px;
      background: rgba(0, 0, 0, 0.8);
      color: #fff;
      border-radius: 10px;
    }

    .active {
      fill: rgba(149, 165, 166, 0.8);
    }

    body {
      background: rgba(32, 32, 32, 1);
      color: rgba(255, 255, 255, 0.8);
    }

    h1 {
      font-family: Impact, Charcoal, sans-serif;
      text-align: center;
    }

    h2 {
      font-family: Impact, Charcoal, sans-serif;
      text-align: center;
    }

    p {
      font-family: "Arial Black", Gadget, sans-serif;
      text-align: center;
    }

    #box {
      border: 10px solid black;
      margin: auto;
      padding: 10px;
      width: 75%;
      border-radius: 10px;
    }

    .map {
      margin-left: auto;
      margin-right: auto;
    }
  </style>
  <script>
    function draw(geo_data) {

      var margin = 0,
        width = 2000 - margin,
        height = 700 - margin;

      var centered;

      var svg = d3.select('#map')
        .append('svg')
        .attr('width', width + margin)
        .attr('height', height + margin)
        .append('g')
        .attr('class', 'map');

      var formatComma = d3.format(",")

      var projection = d3.geoAlbersUsa();

      var path = d3.geoPath().projection(projection);

      var map = svg.selectAll('path')
        .data(geo_data.features)
        .enter()
        .append('path')
        .attr('d', path)
        .attr('fill', 'rgba(105, 105, 105, 1)')
        .attr('stroke', 'rgba(0, 0, 0, 1)')
        .attr('stroke-width', 0.5)
        .on('mouseover', function(d) {
          d3.select(this).attr('fill', 'rgba(108, 122, 137, 0.5)')
        })
        .on('mouseout', function() {
          if (d3.select(this).classed('clicked')) {
            console.log('is clicked')
            d3.select(this).attr('fill', 'rgba(105, 105, 105, 1)')
          } else {
            console.log('is not clicked')
            d3.select(this).attr('fill', 'rgba(105, 105, 105, 1)')
          }
        })
        // Calls click-to-zoom function defined below.
        .on('click', clicked);

      var tip = d3.tip()
        .attr('class', 'd3-tip')
        .offset([-10, 0])
        .html(function(d) {
          return "<p><font size='4rem'>" + d.city + ", " + d.state + "</p></font>" + "<p><font size='3rem'><strong>Guns Manufactured: </strong>" + formatComma(d.guns) + "</p>" +
            "<p><strong>Top Manufacturer:</strong> " + d.manufacturer +
            "</p></font>";
        })

      svg.call(tip);

      // Click-to-zoom function adapted from Mike Bostock's code: https://bl.ocks.org/mbostock/2206590

      function clicked(d) {

        d3.select(this).attr('fill', 'rgba(108, 122, 137, 0.5)');
        if (d3.select(this).classed('clicked')) {
          d3.select(this).attr('clicked', false);
        } else {
          d3.select(this).attr('clicked', true);
        }

        var x, y, k;

        if (d && centered !== d) {
          var centroid = path.centroid(d);
          x = centroid[0];
          y = centroid[1];
          k = 4;
          centered = d;
        } else {
          x = width / 2;
          y = height / 2;
          k = 1;
          centered = null;
        }

        map.selectAll('path')
          .classed('active', centered && function(d) {
            return d === centered;
          });

        map.transition()
          .duration(1000)
          .attr('transform', 'translate(' + width / 2 + ',' + height / 2 + ')scale(' + k + ')translate(' + -x + ',' + -y + ')')
          .style('stroke-width', 1 / k + 'px');

        // Transitions circles upon zoom.

        svg.selectAll('circle')
          .transition()
          .duration(1000)
          .attr('transform', 'translate(' + width / 2 + ',' + height / 2 + ')scale(' + k + ')translate(' + -x + ',' + -y + ')')
          .style('stroke-width', 1 / k + 'px');
      }

      d3.csv('https://raw.githubusercontent.com/dieterholger/US-Gun-Manufacturing-Interactive/master/top100cities.csv', function(error, data) {

        // Converts strings in csv to integers so they can be used.

        data.forEach(function(d) {
          return d.guns = +d.guns;
        })

        // This returns the max number of guns.

        var guns = data.map(function(d) {
          return d.guns;
        });

        var guns_extent = d3.extent(data, function(d) {
          return d.guns;
        });

        var radius = d3.scaleSqrt()
          .domain(guns_extent)
          .range([2, 40]);

        svg.append('g')
          .attr('class', 'bubble')
          .selectAll('circle')
          .data(data)
          .enter()
          .append('circle')
          .attr('cx', function(d) {
            return projection([d.lon, d.lat])[0];
          })
          .attr('cy', function(d) {
            return projection([d.lon, d.lat])[1];
          })
          .attr('r', function(d) {
            return radius(d.guns);
          })
          .attr('fill', 'rgba(248, 148, 6, 0.5)')
          .attr('stroke', 'black')
          .on('mouseover', function(d) {
            tip.show(d);
            return d3.select(this).attr('fill', 'rgba(248, 148, 6, 0.9)');
          })
          .on('mouseout', function(d) {
            tip.hide(d);
            return d3.select(this).attr('fill', 'rgba(248, 148, 6, 0.5)');
          })


      });
    };
  </script>
</head>

<body>

  <div id='box'>
  </div>
  <div id='map'>
    <h2>Hover your mouse to see data on the cities and click a state to zoom in.</h2>
  </div>
  <div id='box'>
  </div>
  <script>
    d3.json('https://raw.githubusercontent.com/dieterholger/US-Gun-Manufacturing-Interactive/master/us_states.json', draw);
  </script>
</body>
  • 1
    As per my answer, your css rules are not quite right. Changing the `.map` rule to be a `#map` rule will partly fix the problem. However, you have both a div with an id of map, and a `` element with a class of map. The outer element (in this case) drives the centering. – Peter Abolins Mar 14 '18 at 14:13

1 Answers1

0

Enclose the map in another div, which has the same width as the box above it (75%). You may want to move the div style to a CSS class, if it works the way you want.

<div style="display:block; margin-left:auto; margin-right:auto; width:75%">
    <div id="map">
    ...
    </div>
</div>

UPDATE

I just found the real problem. Your css definition for map is for a class, not for an id. And... if you want to use auto margins, you need to define a width.

Change your css to match the following:

#map {
  width: 75%;
  margin-left: auto;
  margin-right: auto;
} 
Peter Abolins
  • 1,520
  • 1
  • 11
  • 18