lnNoam
lnNoam

Reputation: 1055

Append SVG items on top of existing elements

I'm trying to add points to a map using the d3.js library. (I've tried to adapt a script from this gist).

The problem is that the map is being rendered on top of the dots. I've tried to use another SO answer which suggested something like this: svg.append("g").attr("id", "map").attr("id", "points")...but I have been unable to make it work. I'm a long time python user...JavaScript is new for me (so please excuse the naivety).

Am using the following CSS:

  <style>
    body {
      background-color: white;
    }
    svg {
        border: 1px solid black;
        background-color: #a4bac7;
    }
        .land {
          fill: #d7c7ad;
          stroke: #8999ab;
        }
        .boundary {
          fill: none;
          stroke: #a5967f;
        }
  </style>

Body:

    <script type="text/javascript" src="d3/d3.js"></script>
    <script src="http://d3js.org/topojson.v0.min.js"></script>

    <script>
        var width = 960;
        var height = 480;
        var dataURL = "https://gist.githubusercontent.com/abenrob/787723ca91772591b47e/raw/8a7f176072d508218e120773943b595c998991be/world-50m.json";

        var svg = d3.select("body").append("svg")
            .attr("width", width)
            .attr("height", height);

In response to this SO answer answer, added this:

        svg.append("g")
                .attr("id", "map")
                .attr("id", "points");

        var projection = d3.geoEquirectangular()
            .scale(153)
            .translate([width/2,height/2])

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

        d3.json(dataURL, function(error, world) {
            svg.append("g")
                .select("#map").selectAll(".map")
                .attr("class", "land")
                .selectAll("path")
                .data([topojson.object(world, world.objects.land)])
                .enter().append("path")
                .attr("d", path);
            svg.append("g")
                .select("#map").selectAll(".map")
                .attr("class", "boundary")
                .selectAll("boundary")
                .data([topojson.object(world, world.objects.countries)])
                .enter().append("path")
                .attr("d", path);
            }
        );

        var lngLatExtract = function(d, i){
            return projection([d['lng'], d['lat']])[i];
        };

        d3.csv("cities.csv", function(data){
                svg.append("g")
                        .select("#points").selectAll(".points")
                        .data(data)
                        .enter()
                            .append('circle')
                            .attr("cx", function(d){
                                return lngLatExtract(d, 0);
                            })
                            .attr('cy', function(d){
                                return lngLatExtract(d, 1);
                            })
                            .style("fill", "blue")
                            .style("opacity", 0.75)
                            .attr("r", function(d){
                                return Math.sqrt(parseInt(d['population']) * 0.000001)
                            });
                }
        );

    </script>

cities.csv looks like this:

rank,place,population,lat,lng
1,New York city,8175133,40.71455,-74.007124
2,Los Angeles city,3792621,34.05349,-118.245323

Upvotes: 1

Views: 942

Answers (1)

Mark
Mark

Reputation: 108567

Both d3.json and d3.csv are async functions. The order in which their callbacks fire is not a given and in fact since your csv file is smaller, it's callback is probably firing first. That means that your svg.append("g") in your csv callback is happening before the svg.append("g") in the json callback. You can confirm this by inspecting the DOM and to see which was appended first.

That said, I would switch to using d3.queue. This allows you to fire off both the d3.json and d3.csv async requests and then fire a single callback with both are complete.

Another less streamlined option is to place your d3.csv code in the callback of the d3.json call.

Upvotes: 2

Related Questions