Plotting Points on a Map with d3.js

2020-07-30 01:33发布

问题:

For this project, I have a map of India in d3, with seperation by province. I'm trying to add points onto the map, but it just isn't working for me.

The nodes are supposed to become bigger and smaller based on the amount of complaints that is specified in the csv file.

Here is my code:

visualize.html

 <!DOCTYPE html>
<html lang="en">
  <head>
    <!-- India State Map  -->
    <title>India Map</title>

    <!--  Scripts  -->
    <script type="text/javascript" src="d3.min.js"></script>
    <script type="text/javascript" src="d3.geo.min.js"></script>
    <style type="text/css">
    svg {
      background: #fff;
      }

    #india {
      fill: #95a5a6;
      opacity: .8;
      stroke: white ;
      stroke-width: 1.2;
      }
    </style>
  </head>

<body>
  <div id="chart"></div>
  <script type="text/javascript">
    var w = 600;
    var h = 600;
    var proj = d3.geo.mercator();
    var path = d3.geo.path().projection(proj);
    var t = proj.translate(); // the projection's default translation
    var s = proj.scale(); // the projection's default scale

    var map = d3.select("#chart").append("svg:svg")
        .attr("width", w)
        .attr("height", h)
    //        .call(d3.behavior.zoom().on("zoom", redraw))
    .call(initialize);

    var india = map.append("svg:g")
        .attr("id", "india");



    d3.json("json/indianstates.json", function (json) {
        india.selectAll("path")
            .data(json.features)
            .enter().append("path")
            .attr("d", path);
    });

 d3.csv("csv/water.csv", function (data) {

        svg.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 Math.sqrt(parseInt(d.complaints)*0.0004);
        })
            .style("fill", "yellow")
            .style("opacity", 0.75);

    });
    function initialize() {
        proj.scale(6700);
        proj.translate([-1240, 720]);
    }

Here is the CSV file. I will not include the json file with the state boundaries because it is working fine. I have no clue whether the problem lies within the formatting of the csv file, the way the file is called in the script, or if it is just some error in the script.

water.csv

lon,lat,quality,complaints
80.06,20.07,4,17
72.822,18.968,2,62
77.216,28.613,5,49
92.79,87.208,4,3
87.208,21.813,1,12
77.589,12.987,2,54
16.320,75.724,4,7

EDIT: Fixed some of the things pointed out to me.

回答1:

Did you get any output with your code, as you've got a couple of syntax errors, such as:

  1. proj vs projection (in the calculation of the circle x and y positions)
  2. svg vs india (the variable svg doesn't exist when you try to create the circles)

After correcting those syntax errors, your code still doesn't work as expected, with nothing visible in the browser, probably because of:

  1. The projection needs to be rotated to bring the map into view
  2. The projection needs a scale and translate that matches the map and svg size
  3. The circles you draw are going to be really small due to the multiplication by 0.0004

Here are some changes that may get you onto the right track:

<!DOCTYPE html>
<html lang="en">
    <head>
    <!-- India State Map  -->
    <title>India Map</title>
    <style type="text/css">
        svg {
            background: #fff;
        }

        #india {
            fill: #95a5a6;
            opacity: .8;
            stroke: white ;
            stroke-width: 1.2;
        }
    </style>
</head>

<body>
    <div id="chart"></div>
    <script type="text/javascript" src="//cdnjs.cloudflare.com/ajax/libs/d3/3.4.13/d3.min.js"></script>
    <script type="text/javascript">
        var w = 600;
        var h = 600;
        var bounds = [[68,38], [97, 8]];  // rough extents of India
        var proj = d3.geo.mercator()
                         .scale(800)
                         .translate([w/2,h/2])
                         .rotate([(bounds[0][0] + bounds[1][0]) / -2, 
                                  (bounds[0][1] + bounds[1][1]) / -2]); // rotate the project to bring India into view.

        var path = d3.geo.path().projection(proj);

        var map = d3.select("#chart").append("svg:svg")
                    .attr("width", w)
                    .attr("height", h);

        var india = map.append("svg:g")
                       .attr("id", "india");

        d3.json("json/indianstates.json", function(json) {
            india.selectAll("path")
                 .data(json.features)
               .enter().append("path")
                 .attr("d", path);
        });

        d3.csv("csv/water.csv", function(csv) {
            india.selectAll("circle")
                 .data(csv)
               .enter()
                 .append("circle")
                 .attr("cx", function (d) {
                     return proj([d.lon, d.lat])[0];
                 })
                 .attr("cy", function (d) {
                     return proj([d.lon, d.lat])[1];
                 })
                 .attr("r", function (d) {
                     return Math.sqrt(parseInt(d.complaints));
                 })
                 .style("fill", "yellow")
                 .style("opacity", 0.75);
        });

    </script>
</body>

You can see where I've rotated the projection based upon the geographical extents of India, set the scale and translate to appropriate values based on the size of the SVG and the size of the country. I've also set the radius of the circles to more appropriate values.

Note: I used an arbitrary json/indianstates.json file that I googled for, hopefully it's the same as, or close enough to your file.