Carisa
Carisa

Reputation: 33

D3 map, click and get id from json

I have D3 code to make a U.S. map based off arcs in a JSON file. The code I am using is based off this example (http://bl.ocks.org/mbostock/4108203) where this is the json file (http://bl.ocks.org/mbostock/raw/4090846/us.json)

<!DOCTYPE html>
<meta charset="utf-8">
<style>

path {
  fill: none;
  stroke: #000;
  stroke-width: .5px;
}

.land-boundary {
  stroke-width: 1px;
}

.county-boundary {
  stroke: #aaa;
    }

</style>
<body>
<script src="//d3js.org/d3.v3.min.js"></script>
<script src="//d3js.org/topojson.v1.min.js"></script>
<script>

var width = 960,
    height = 500;

var path = d3.geo.path();

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

d3.json("js/us.json", function(error, topology) {
  if (error) throw error;

  svg.append("path")
      .datum(topojson.feature(topology, topology.objects.land))
      .attr("d", path)
      .attr("class", "land-boundary");


  svg.append("path")
      .datum(topojson.mesh(topology, topology.objects.states, function(a, b) { return a !== b; }))
      .attr("d", path)
      .attr("class", "state-boundary");
});

</script>

How would I click on the map and return the ID of the state that was clicked on?

The first console.log right now gives me the pixel coordinates of where I'm clicking, the second gives back an svg object and select gives back a parent node..?

d3.select("svg").on("mousedown.log", function() {
          console.log(d3.mouse(this));
          console.log(this);
          console.log(d3.select("id")[0]);
    });

The json looks like it has an object "states" with a dictionary including the arcs to make the map and the id of the state which corresponds to a state in this list (https://gist.github.com/mbostock/4090846#file-us-state-names-tsv). I just can't figure out the right function to use to isolate the ID corresponding to the object.

Upvotes: 3

Views: 3018

Answers (1)

iH8
iH8

Reputation: 28638

First off, you're creating a mesh of your features, which will turn all of the features into a multilinestring which doesn't hold any data. If you want to have events on the individual states and retain the data, you'll need to use .feature instead of .mesh

topojson.mesh:

Returns the GeoJSON MultiLineString geometry object representing the mesh for the specified object in the given topology. This is useful for rendering strokes in complicated objects efficiently, as edges that are shared by multiple features are only stroked once.

https://github.com/mbostock/topojson/wiki/API-Reference#mesh

topojson.feature:

Returns the GeoJSON Feature or FeatureCollection for the specified object in the given topology. If the specified object is a GeometryCollection, a FeatureCollection is returned, and each geometry in the collection is mapped to a Feature. Otherwise, a Feature is returned.

https://github.com/mbostock/topojson/wiki/API-Reference#feature

Next you're binding your eventlistener to the SVG. If you bind it to the actual paths you're creating you have got direct access to the data object like Lars mentioned in his comment on your question:

svg.append("g")
    .selectAll("path")
        .data(topojson.feature(us, us.objects.states).features)
    .enter().append("path")
        .attr("class", "state-boundary")
        .attr("d", path)
        .style("fill", "red")
        .on('mousedown.log', function (d) {
            console.log(d.id);
        });

If you want to bind to the SVG and access the data you can do this but i wouldn't recommend it, but just to show it is possible:

d3.select("svg").on("mousedown.log", function() {
    console.log(d3.event.srcElement.__data__.id);
});

Upvotes: 1

Related Questions