1

I'm trying to create a graduated symbol map and am struggling to find a way to make this happen. I can create pie charts and I can create a symbol map, but how to place pie charts at specific coordinates on a map?

I've successfully placed proportional symbols at the proper coordinates, but I can't figure out how to replace the symbols with pie charts. Every attempt leaves me with an empty map.

I've tried to merge Mike Bostock's Pie Multiples example with his Symbol Map example but have instead only managed to expose my lack of understanding of d3's data and event functions.

Index.html

<!DOCTYPE html>
<html>
<head>
    <meta http-equiv="Content-Type" content="text/html;charset=utf-8"/>
    <title>Graduated Symbol Map</title>
    <script type="text/javascript" src="http://d3js.org/d3.v3.min.js"></script>
    <script type="text/javascript" src="http://d3js.org/topojson.v1.min.js"></script>
    <script type="text/javascript" src="http://d3js.org/queue.v1.min.js"></script>
    <style type="text/css">

body {
text-align: center;
}

    </style>
</head>
<body>
    <script type="text/javascript">

var width = 400,
    height = 500;

var radius = d3.scale.sqrt()
    .domain([0, 5e5])
    .range([0, 40]);

// Define map projection
var projection = d3.geo.transverseMercator()
    .rotate([72.57, -44.20])
    .translate([175,190])
    .scale([12000]);

// Define path generator
var path = d3.geo.path()
    .projection(projection);

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

queue()
    .defer(d3.json, "vermont.json")
    .defer(d3.json, "fed.json")
    .await(ready)

function ready(error, vt, centroid) {
    svg.append("path")
        .attr("class", "towns")
        .datum(topojson.feature(vt, vt.objects.vt_towns))
        .attr("d", path)
        .style("stroke", "#ddd")
        .style("stroke-width", "1px")
        .style("fill", "#ccc");

    svg.append("path")
        .datum(topojson.feature(vt, vt.objects.lake))
        .attr("d", path)
        .style("stroke", "#89b6ef")
        .style("stroke-width", "1px")
        .style("fill", "#b6d2f5");

    svg.selectAll(".symbol")
        .data(centroid.features.sort(function(a,b) {
            return b.properties.dollars - a.properties.dollars; }))
        .enter().append("path")
            .attr("class", "symbol")
            .attr("d", path.pointRadius(function(d) {
                return radius(d.properties.dollars); })
            )
            .style("fill", "#509e2f")
            .style("stroke", "#ddd")
            .style("fill-opacity", 0.7);
}

    </script>
</body>
</html>        

fed.json (there are 14 points, all with the same format)

'dollars' are the total dollars spent by the four organizations, the size of the pie chart should relate to this value.

{
    "type": "Feature",
    "id": "53",
    "geometry": {
        "type": "Point",
        "coordinates": [-73.1349605, 43.0278745]
    },
    "properties": {
        "name": "Bennington County",
        "dollars": 79730,
        "unit": "county",
        "ECP": 49608,
        "LIP": 3451,
        "NAP": 0,
        "SURE": 26671
    }
}, 

vermont.json

Large file, map is not the issue.

References I've used

Matt Parrilla
  • 3,171
  • 6
  • 35
  • 54
  • 1
    If you look at the multiple pie charts example, you'll notice that there's a separate `g` element for each pie chart. This is the key to achieving what you want. For each of these `g` elements, you can set a suitable `transform` attribute to move it where you want. The coordinates for that can be part of the data. – Lars Kotthoff Aug 01 '13 at 08:25
  • @LarsKotthoff hmm. Will I not be able to set the `d` attribute equal to path? I am unfamiliar with the `transform` attribute and am unsure how I would translate a set of coordinates into the necessary value. – Matt Parrilla Aug 01 '13 at 14:39
  • Figured it out using a [previous answer](http://stackoverflow.com/a/16790390/723998) you posted! I'll post my exact solution shortly. – Matt Parrilla Aug 01 '13 at 19:36

1 Answers1

2

Here's my solution, using @LarsKotthoff's answer from this question to solve the projection issue.

I've scaled the pie charts in a rather hackish way.

index.html

Below is just the ready function. Everything else has remained unchanged.

function ready(error, vt, centroid) {
    svg.append("path")
        .attr("class", "towns")
        .datum(topojson.feature(vt, vt.objects.vt_towns))
        .attr("d", path)
        .style("stroke", "#ddd")
        .style("stroke-width", "1px")
        .style("fill", "#ccc");

    svg.append("path")
        .datum(topojson.feature(vt, vt.objects.lake))
        .attr("d", path)
        .style("stroke", "#89b6ef")
        .style("stroke-width", "1px")
        .style("fill", "#b6d2f5");

    var pieArray = [],
        pieMeta = [];

    function pieData() {
        for (var i=0; i<centroid.features.length; i++) {
            pieArray.push([
                parseInt(centroid.features[i].properties.ECP),
                parseInt(centroid.features[i].properties.LIP),
                parseInt(centroid.features[i].properties.NAP),
                parseInt(centroid.features[i].properties.SURE)
                ]);
            pieMeta.push([
                projection(centroid.features[i].geometry.coordinates),
                radius(parseInt(centroid.features[i].properties.dollars))
                ]);
        }
        return [pieArray, pieMeta];
    };

    var svgSvg = d3.select("body").select("svg").selectAll("g")
            .data(pieData()[0])
        .enter().append("svg:svg")
            .attr("width", width)
            .attr("height", height)
        .append("svg:g")
            .style("opacity", 0.8)
            .attr("property", function (d,i) {
                return pieData()[1][i][1];
            })
            .attr("transform", function (d,i) {
                var coordinates = pieData()[1][i][0];
                return ("translate(" + (coordinates[0]) + "," +
                    (coordinates[1]) + ")");
                });

    svgSvg.selectAll("path")
            .data(d3.layout.pie())
        .enter().append("svg:path")
            .attr("d", d3.svg.arc()
                .innerRadius(0)
                .outerRadius(function (d) {
                    var chartList = d3.select(this.parentNode).attr("property");
                    return chartList;
                }))
            .style("fill", function(d, i) { return color[i]; });

}
Community
  • 1
  • 1
Matt Parrilla
  • 3,171
  • 6
  • 35
  • 54