How to add Label to each state in d3.js (albersUsa)? - javascript

The us.json loaded, but when i try to add Label name i can't make it work. I don't see the name property in .json file so how can i add each state name? I'm really new to this framework.
I try different Tutorial on Google and Stackoverflow, but none of them work for me. Here is the link to couple tutorial i tried, that i think is worthy.
Add names of the states to a map in d3.js
State/County names in TopoJSON or go back GeoJSON?
The concerns I have:
I think I'm missing name property in us.json file. (if that's the issue, is there any other .json file that includes state name? And how to use the state name with that file?)
Is the US state name included in http://d3js.org/topojson.v1.min.js?
.html File (Framework Loaded)
<script src="http://d3js.org/d3.v3.min.js" charset="utf-8"></script>
<script src="http://d3js.org/topojson.v1.min.js"></script>
.js File:
var width = 1500,
height = 1100,
centered;
var usData = ["json/us.json"];
var usDataText = ["json/us-states.json"];
var projection = d3.geo.albersUsa()
.scale(2000)
.translate([760, height / 2]);
var path = d3.geo.path()
.projection(projection);
var svg = d3.select("body").append("svg")
.style("width", "100%")
.style("height", "100%");
svg.append("rect")
.attr("class", "background")
.attr("width", width)
.attr("height", height)
.on("click", clicked);
var g = svg.append("g");
d3.json(usData, function(unitedState) {
g.append("g")
.attr("class", "states-bundle")
.selectAll("path")
.data(topojson.feature(unitedState, unitedState.objects.states).features)
.enter()
.append("path")
.attr("d", path)
.attr("class", "states")
.on("click", clicked);
});
Thank you everyone in advanced. I also appreciate if you tell me where did you learn d3.js.

As you stated your us.json doesn't have state names in it. What it has, though, are unique ids and luckily, Mr. Bostock has mapped those ids to names here.
So, let's fix up this code a bit.
First, make the json requests to pull the data:
// path data
d3.json("us.json", function(unitedState) {
var data = topojson.feature(unitedState, unitedState.objects.states).features;
// our names
d3.tsv("us-state-names.tsv", function(tsv){
// extract just the names and Ids
var names = {};
tsv.forEach(function(d,i){
names[d.id] = d.name;
});
Now add our visualization:
// build paths
g.append("g")
.attr("class", "states-bundle")
.selectAll("path")
.data(data)
.enter()
.append("path")
.attr("d", path)
.attr("stroke", "white")
.attr("class", "states");
// add state names
g.append("g")
.attr("class", "states-names")
.selectAll("text")
.data(data)
.enter()
.append("svg:text")
.text(function(d){
return names[d.id];
})
.attr("x", function(d){
return path.centroid(d)[0];
})
.attr("y", function(d){
return path.centroid(d)[1];
})
.attr("text-anchor","middle")
.attr('fill', 'white');
....
Here's a working example.

Related

d3js v5 + Topojson v3 Map not rendering

I don't know why, according to the topojson version (may be) I have:
TypeError: t is undefined
An explanation could be nice! (I use the last version of topojson.)
Here an example of TypeError is undefined (pointing to topojson file)
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" href="style.css">
<script src="https://unpkg.com/d3#5.0.0/dist/d3.min.js"></script>
<script src="https://d3js.org/d3-scale-chromatic.v1.min.js"></script>
<script src="https://d3js.org/topojson.v2.min.js"></script>
</head>
<body>
<svg width="960" height="600"></svg>
<script>
var svg = d3.select("svg"),
width = +svg.attr("width"),
height = +svg.attr("height");
var unemployment = d3.map();
var path = d3.geoPath();
var x = d3.scaleLinear()
.domain([1, 10])
.rangeRound([600, 860]);
var color = d3.scaleThreshold()
.domain(d3.range(2, 10))
.range(d3.schemeBlues[9]);
var g = svg.append("g")
.attr("class", "key")
.attr("transform", "translate(0,40)");
g.selectAll("rect")
.data(color.range().map(function(d) {
d = color.invertExtent(d);
if (d[0] == null) d[0] = x.domain()[0];
if (d[1] == null) d[1] = x.domain()[1];
return d;
}))
.enter().append("rect")
.attr("height", 8)
.attr("x", function(d) { return x(d[0]); })
.attr("width", function(d) { return x(d[1]) - x(d[0]); })
.attr("fill", function(d) { return color(d[0]); });
g.append("text")
.attr("class", "caption")
.attr("x", x.range()[0])
.attr("y", -6)
.attr("fill", "#000")
.attr("text-anchor", "start")
.attr("font-weight", "bold")
.text("Unemployment rate");
g.call(d3.axisBottom(x)
.tickSize(13)
.tickFormat(function(x, i) { return i ? x : x + "%"; })
.tickValues(color.domain()))
.select(".domain")
.remove();
var files = ["https://d3js.org/us-10m.v1.json", "unemployment.tsv"];
var promises1 = d3.json("https://d3js.org/us-10m.v1.json");
var promises2 = d3.tsv("unemployment.tsv");
Promise.all([promises1, promises2]).then(function(us){
console.log(us[0]);
console.log(us[1]);
svg.append("g")
.attr("class", "counties")
.selectAll("path")
.data(topojson.feature(us, us[0].objects.counties).features)
.enter().append("path")
.attr("fill", function(d) { return color(d.rate = unemployment.get(d.id)); })
.attr("d", path)
.append("title")
.text(function(d) { return d.rate + "%"; });
svg.append("path")
.datum(topojson.mesh(us, us[0].objects.states, function(a, b) { return a !== b; }))
.attr("class", "states")
.attr("d", path);
});
</script>
</body>
</html>
My code : https://plnkr.co/edit/EzcZMSEQVzCt4uoYCLIc?p=info
Original (d3js v4 + Topojson v2) : https://bl.ocks.org/mbostock/4060606
Here an another example of TypeError is undefined (pointing to topojson file)
My code : https://plnkr.co/edit/o1wQX3tvIDVxEbDtdVZP?p=preview
The two examples have two separate issues in relation to topojson.
In the first example you update where the topojson is held from us to us[0] due to the change in how files are fetched. However, you haven't quite updated the code to reflect this change:
In Original: .data(topojson.feature(us, us.objects.counties).features)
In Question: .data(topojson.feature(us, us[0].objects.counties).features)
And fixed: .data(topojson.feature(us[0], us[0].objects.counties).features)
Updated plunkr.
However, the issue in the second example is a little different.
topojson.feature requires two parameters, a topology and an object. The topology is the variable holding the json, which you have correct. However, the object is not arcs. The variable holding the topojson has a property called objects, and in that there will always be at least one property representing a feature collection (states, counties, etc). This object (or one of these objects) is what we want.
Here is a snippet of your topojson:
... "objects":{"dep_GEN_WGS84_UTF8":{"type":"GeometryCollection","geometries":[{"arcs ..."
We want topojson.feature(data,data.objects.dep_GEN_WGS84_UTF8).
If making topojson with tools such as mapshaper, the object we want to display is the same as the name of the file used to create it. Generally, a quick word search through the topojson for "object" will also get you to the proper object pretty quick.
The arcs property in a topojson is convenient storage for the pieces that make up the features, not the features themselves.
Updated plunkr.
In both cases the topology parameter passed to topojson.feature won't contain the specified features, generating the same error.

Drawing states separately with d3 js and country topojson file

I have a topojson which contains state's paths. I want the user to be able to hover over a state and the state to appear in a different svg. So far, I've tried to extract the geometry out of the topojson (d.geometry , d.geometry.coordinates etc) But I'm not able to do it.
Maybe I need to draw a polygon out of that, but some states are of type "Polygon" and some of them are of type "MultiPolgyon".
Any ideas/suggestions?
Edit : Here's my code
var svg = d3.select("#india-map")
.append("svg")
.attr("width",width).attr("preserveAspectRatio", "xMidYMid")
.attr("viewBox", "0 0 " + width + " " + height)
.attr("height", height)
var stateSvg = d3.select("#state-map")
.append("svg")
.append("g")
.attr("height", height)
.attr("width", width);
var g = svg.append("g");
var projection = d3.geo.mercator()
.center([86, 27])
.scale(1200);
var path = d3.geo.path().projection(projection);
var pc_geojson = topojson.feature(pc, pc.objects.india_pc_2014);
var st_geojson = topojson.feature(state_json, state_json.objects.india_state_2014);
g.selectAll(".pc")
.data(pc_geojson.features)
.enter().append("path")
.attr("class", "pc")
.attr("d", path)
.attr("id", function(d){ return d.properties.Constituency;})
.attr("fill", "orange")
.on("click", function(d){
drawCons(d);
});
function drawCons(d){
stateSvg.selectAll(".pc2")
.data(d)
.enter().append("path")
.attr("class","pc2")
.attr("d", path)
}
.data() expects to be given an array of objects to be matched against the selection. You're passing a single object, so it doesn't work. You can either use .datum(d) or .data([d]) to make it work.
Quick and dirty demo here.

d3.js create objects on top of each other

I create rectangles in my SVG element using this code:
var rectangles = svg.selectAll("rect")
.data(dataset)
.enter()
.append("rect");
rectangles.attr("x", function (d) {
return xScale(getDate(d));
//return xScale(d.start);
})
.attr("y", function (d, i) {
return (i * 33);
})
.attr("height", 30)
.transition()
.duration(1000)
.attr("width", function (d) {
return d.length;
})
.attr("rx", 5)
.attr("ry", 5)
.attr("class", "rectangle")
.attr("onclick", function (d) {
return "runaction(" + d.start + ")";
});
How can I create new rectangles on top of the previous ones?
This is an answer to this question I got from Scott Murray, author of great introductory tutorials for d3.js http://alignedleft.com/tutorials/d3/ which helped me a lot with understanding its functionality. I hope he won't mind me putting his answer here for everyone's benefit.
Thank you very much Scott!
And yes, that's absolutely possible. Taking your example, let's say you want to draw one set of circles with the dataset called "giraffeData" bound to them. You would use:
svg.selectAll("circle")
.data(giraffeData)
.enter()
.append("circle");
But then you have a second data set (really just an array of values) called "zebraData". So you could use the same code, but change which data set you reference here:
svg.selectAll("circle")
.data(zebraData)
.enter()
.append("circle");
Of course, this will inadvertently select all the circles you already created and bind the new data to them — which isn't really what you want. So you'll have to help D3 differentiate between the giraffe circles and the zebra circles. You could do that by assigning them classes:
svg.selectAll("circle.giraffe")
.data(giraffeData)
.enter()
.append("circle")
.attr("class", "giraffe");
svg.selectAll("circle.zebra")
.data(zebraData)
.enter()
.append("circle")
.attr("class", "zebra");
Or, you could group the circles of each type into a separate SVG 'g' element:
var giraffes = svg.append("g")
.attr("class", "giraffe");
giraffes.selectAll("circle")
.data(giraffeData)
.enter()
.append("circle");
var zebras = svg.append("g")
.attr("class", "zebra");
zebras.selectAll("circle")
.data(zebraData)
.enter()
.append("circle");
I'd probably choose the latter, as then your DOM is more cleanly organized, and you don't have to add a class to every circle. You could just know that any circle inside the g with class zebra is a "zebra circle".

d3.js GeoJSON transitions

I have created two GeoJSON objects, and when I inspect the element I can see them both listed. I have looked through numerous transition tutorials on d3.js and am still lost as to how to draw one, then transition to the other.
I have tried to implement the exit().remove() function within the second geoJSON object, but have failed to make this work. I am very much lost, and cannot seem to make anything I have found work.
I understand that the code below is very basic, but all the things I have tried have failed beyond this point.
<head>
<meta charset="utf-8">
<title>Cartogram 1</title>
<style>
</style>
<svg width="100" height="2"></svg>
<script type="text/javascript" src="js/d3.v3.js"></script>
<script>
var svg = d3.select("body").append("svg")
.attr("width", 1500)
.attr("height", 1500)
var projection = d3.geo.equirectangular();
var world1 = d3.json("world2.json", function (data) {
svg.selectAll("g")
.data(data.features)
.enter()
.append("g")
.append("path")
.attr("d", path)});
var world2 = d3.json("goldatt2.json", function (data) {
svg.selectAll("h")
.data(data.features)
.enter()
.append("h")
.append("path")
.attr("d", path)});
path = d3.geo.path().projection(projection);
</script>
This has been changed to:
var svg = d3.select("body").append("svg")
.attr("width", 1500)
.attr("height", 1500)
var projection = d3.geo.equirectangular()
var path = d3.geo.path().projection(projection);
d3.json("world2.json", function (data) {
svg.selectAll("g")
.data(data.features)
.enter()
.append("g")
.append("path")
.attr("d", path)
});
d3.json("goldq.json", function (data) {
var sel = svg.selectAll("g")
.data(data.features);
sel.enter()
.append("g")
.append("path");
sel.transition().duration(1000).attr("d", path)
sel.exit().remove();
});
Which results in the following:
http://www.geos.ed.ac.uk/~s1227289/world/lars.html
What am I missing?
You need to update the path once you've drawn it:
var path = d3.geo.path().projection(projection);
d3.json("world2.json", function (data) {
svg.selectAll("g")
.data(data.features)
.enter()
.append("g")
.append("path")
.attr("d", path)
});
d3.json("goldatt2.json", function (data) {
var sel = svg.selectAll("g")
.data(data.features);
sel.enter()
.append("g")
.append("path");
sel.attr("d", path);
sel.exit().remove();
});
A few general notes. There is no h element. In the code above, the order of execution is not guaranteed as d3.json is asynchronous. You need to make sure that it executes in the order you want yourself, for example by nesting the calls to d3.json.

Very Simple D3: How to Draw an Arc?

It would be nice to learn D3. After reading many examples, I think I understand it. My first project is to make a color wheel, without transitions for simplicity. But it appears even that is not simple enough for my first project! For project zero, I am trying to get something to show on the screen. Hopefully something I wrote (and dear read has fixed), and not an example.
What did I do wrong? http://jsfiddle.net/aGdMX/1/
var arc = d3.svg.arc()
.innerRadius(40)
.outerRadius(100)
.startAngle(0)
.endAngle(1)
;
var chart = d3.select("body").append("svg:svg")
.attr("class", "chart")
.attr("width", 420)
.attr("height", 420).append("svg:g")
.attr("transform", "translate(200,200)")
;
chart.selectAll("path")
.data(data)
.enter().append("svg:path")
.attr("fill", function(d, i){
return d3.rgb("black");
})
.attr("d", arc)
;
Thank you
Your example here doesn't have any data defined. If you just want to draw the svg statically, skip the selectAll() and data() bindings:
chart
.append("svg:path")
.attr("fill", function(d, i){
return d3.rgb("black");
})
.attr("d", arc)
;
Or define some data and use that to drive the drawing:
http://jsfiddle.net/findango/aGdMX/2/
(plus .attr("fill"... should be .style("fill"...)

Categories