xxxxxxxxxx
<meta charset="utf-8">
<style>
body {
font-family: "Helvetica Neue", Arial;
font-weight: 200;
font-size: 12px;
}
.counties {
stroke: #1d1e1a;
stroke-width: .1;
fill: #fff;
}
.usCounties {
stroke: #1d1e1a;
stroke-width: .1;
fill: #bfbfbf;
}
.states {
stroke: #1d1e1a;
stroke-width: .5;
fill: #efefef;
}
.bubbles {
stroke: #1d1e1a;
fill-opacity: .95;
}
.links {
stroke: #1d1a1a;
stroke-width: 1px;
opacity: .95;
stroke-dasharray: 5;
}
.outline {
stroke: #000;
stroke-width: 3;
}
.text {
pointer-events: none;
}
svg {
overflow: visible;
}
.headline {
font-size: 32px;
text-anchor: "end";
}
.subline {
text-anchor: "end";
}
</style>
<body>
<div id="chart-container"> </div>
<script src="https://d3js.org/d3.v4.min.js"></script>
<script src="https://d3js.org/topojson.v1.min.js"></script>
<script src="https://d3js.org/d3-scale-chromatic.v1.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/queue-async/1.0.7/queue.min.js"></script>
<script>
var margin = {top: 10, right: 20, bottom: 50, left: 20};
var width = 900 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
var coordinateLookup = {
"Waseca": { "lat": 44.077591, "long": -93.505882 },
"Duluth": { "lat":46.795114,"long": -92.100090},
"GrandRapids": { "lat":47.235844,"long": -93.527529},
"Morris": { "lat":45.979908,"long": -94.278796},
"Crookston": { "lat":47.77468,"long": -96.606201},
"UniversityFarm": { "lat":44.984557,"long": -93.185641}
}
var simulation = d3.forceSimulation()
.force("charge", d3.forceManyBody().strength(-100))
.force("link", d3.forceLink().id(function(d) { return d.id; }))
.force("x", d3.forceX(width / 2))
.force("y", d3.forceY(height / 2));
var svg = d3.select("#chart-container")
.append("svg")
.attr("class", "map")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
queue()
.defer(d3.tsv, "barley.tsv")
.defer(d3.json, "us.json")
.defer(d3.json, "tl_2013_27_cousub.json")
.await(ready);
function ready(error, barley, us, mn) {
if (error) throw error;
console.log(mn);
// COUNTIES SECTION
var counties = topojson.feature(mn, mn.objects.tl_2013_27_cousub);
var usStates = topojson.feature(us, us.objects.states);
var projection = d3.geoAlbersUsa()
.fitSize([width, height], counties);
var path = d3.geoPath()
.projection(projection);
svg.selectAll(".states")
.data(usStates.features)
.enter().append("path")
.attr("class", "states")
.attr("d", path)
svg.append("path")
.datum(topojson.mesh(mn, mn.objects.tl_2013_27_cousub))
.attr("class", "outline")
.attr("d", path)
var countyPaths = svg.selectAll(".counties")
.data(counties.features)
.enter().append("path")
.attr("class", "counties")
.attr("d", function(d) { return path(d); })
// TEXT
svg.append("text")
.attr("class", "headline")
.attr("x", width - width*.1)
.attr("y", height*.1)
.text("Michigan Barley")
svg.append("text")
.attr("class", "subline")
.attr("x", width - width*.1)
.attr("y", height*.15)
.text("1931-1932")
// BARLEY SECTION
barley.forEach(function(d) {
d.site = d.site.replace(" ","").replace(". ", "")
d.variety = d.variety.replace(" ","").replace(". ", "")
d["third_level_node"] = d.site + "-" + d.variety + "-" + d.year;
d["second_level_node"] = d.site + "-" + d.variety
d["first_level_node"] = d.site
})
var nestedBarley = d3.nest()
.key(function(d) { return d.site })
.key(function(d) { return d.variety })
.entries(barley);
var yoyLookup = {};
var secondYearLookup = {};
nestedBarley.forEach(function(site) {
site.values.forEach(function(variety) {
variety["YoY"] = variety.values[1].yield - variety.values[0].yield;
yoyLookup[site.key + "-" + variety.key] = variety["YoY"]
secondYearLookup[site.key + "-" + variety.key] = variety.values[1].yield;
})
});
var sites = d3.set(barley.map(function(d) { return d.first_level_node; })).values(),
siteVarieties = d3.set(barley.map(function(d) { return d.second_level_node; })).values();
var nodes = [],
links = [];
sites.forEach(function(site) {
var lat = coordinateLookup[site].lat;
var long = coordinateLookup[site].long;
nodes.push({"id": site,
"type": "site",
"site": site,
"displayName": site,
"fx": projection([long,lat])[0],
"fy": projection([long,lat])[1]
})
});
siteVarieties.forEach(function(siteVariety) {
nodes.push({"id": siteVariety,
"type": "Variety",
"site": siteVariety.split("-")[0],
"displayName": siteVariety.split("-")[1]
})
links.push({"source": siteVariety, "target": siteVariety.split("-")[0]})
});
var graph = {
"nodes": nodes,
"links": links
}
var link = svg.append("g")
.attr("class", "links")
.selectAll("line")
.data(graph.links)
.enter().append("line");
var radiusScale = d3.scaleLinear()
.domain(d3.extent(barley, function(d) { return d.yield; }))
.range([2, 20])
var colorScale = d3.scaleSequential(d3.interpolateRdYlGn)
.domain([-25, 25])
var node = svg.append("g")
.attr("class", "nodes")
.selectAll("circle")
.data(graph.nodes)
.enter().append("circle")
.attr("r", 2)
.attr("class", "site")
node.filter(function(d) { return d.type == "Variety" })
.attr("class", "bubbles")
.attr("r", function(d) { return radiusScale(secondYearLookup[d.id]); })
.style("fill", function(d) { return colorScale(yoyLookup[d.id]) })
.call(d3.drag()
.on("start", dragstarted)
.on("drag", dragged)
.on("end", dragended))
.on("mouseenter", function(d) {
d3.select(".nodes")
.style("opacity", .5);
d3.select(".links")
.style("opacity", .5);
d3.select(".text")
.selectAll("text")
.filter(function(e) { return e.site == d.site && e.displayName == d.displayName; })
.style("opacity", 1)
})
.on("mouseleave", function(d) {
d3.select(".nodes")
.style("opacity", .95);
d3.select(".links")
.style("opacity", .95);
d3.select(".text")
.selectAll("text")
.filter(function(e) { return e.site == d.site && e.displayName == d.displayName; })
.style("opacity", 0)
});
var text = svg.append("g")
.attr("class", "text")
.selectAll("text")
.data(graph.nodes)
.enter().append("text")
.attr("dy", 4)
.attr("dx",function(d) { return radiusScale(secondYearLookup[d.id])+1; })
.style("opacity", 0)
.text(function(d) { return d.displayName;})
simulation
.nodes(graph.nodes)
.on("tick", ticked);
simulation.force("link")
.links(graph.links);
function ticked() {
link
.attr("x1", function(d) { return d.source.x; })
.attr("y1", function(d) { return d.source.y; })
.attr("x2", function(d) { return d.target.x; })
.attr("y2", function(d) { return d.target.y; });
node
.attr("cx", function(d) { return d.x; })
.attr("cy", function(d) { return d.y; });
text
.attr("x", function(d) { return d.x; })
.attr("y", function(d) { return d.y; });
}
}
function dragstarted(d) {
if (!d3.event.active) simulation.alphaTarget(0.3).restart();
d.fx = d.x;
d.fy = d.y;
}
function dragged(d) {
d.fx = d3.event.x;
d.fy = d3.event.y;
}
function dragended(d) {
if (!d3.event.active) simulation.alphaTarget(0);
d.fx = null;
d.fy = null;
}
</script>
</html>
https://d3js.org/d3.v4.min.js
https://d3js.org/topojson.v1.min.js
https://d3js.org/d3-scale-chromatic.v1.min.js
https://cdnjs.cloudflare.com/ajax/libs/queue-async/1.0.7/queue.min.js