|
<!DOCTYPE html> |
|
<html> |
|
<head> |
|
<meta charset="utf-8"> |
|
<title>Dynamic Size Example</title> |
|
<script src="https://d3js.org/d3.v4.min.js"></script> |
|
<script src="https://d3js.org/topojson.v2.min.js"></script> |
|
<style> |
|
|
|
/* Make the chart container fill the page using CSS. */ |
|
#chart { |
|
position: fixed; |
|
left: 0px; |
|
right: 0px; |
|
top: 0px; |
|
bottom: 0px; |
|
} |
|
|
|
.area { |
|
stroke: #fff; |
|
stroke-width: 1.5px; |
|
opacity: 0.7; |
|
fill: #aaa; |
|
} |
|
|
|
.area:hover { |
|
opacity: 1.0; |
|
} |
|
|
|
.alreadyRun { |
|
fill: darkgreen; |
|
} |
|
</style> |
|
</head> |
|
<body> |
|
|
|
<div id="chart"></div> |
|
|
|
<script> |
|
|
|
const chartDiv = document.getElementById("chart"); |
|
const svg = d3.select(chartDiv).append("svg"); |
|
const scaleColor = d3.scaleLinear(); |
|
|
|
function getMapScale(width, height) { |
|
// known size of CT image for given scale |
|
const baseScale = 12000; |
|
const baseWidth = 453; |
|
const baseHeight = 379; |
|
|
|
const scale1 = baseScale*width/baseWidth; |
|
const scale2 = baseScale*height/baseHeight; |
|
return d3.min([scale1, scale2]); |
|
} |
|
|
|
const drivingTimesMap = {}; |
|
build_driving_map = row => { |
|
drivingTimesMap[row.Town] = {}; |
|
drivingTimesMap[row.Town].time = +row.DrivingTime; |
|
const hours = Math.floor(+row.DrivingTime/60); |
|
const mins = +row.DrivingTime - 60*hours; |
|
if(hours > 0) { |
|
drivingTimesMap[row.Town].timeString = hours + "h " + mins + " min"; |
|
} else { |
|
drivingTimesMap[row.Town].timeString = mins + " min"; |
|
} |
|
return row; |
|
}; |
|
|
|
const racesRunMap = {}; |
|
build_races_run_map = row => { |
|
racesRunMap[row.Town] = {}; |
|
racesRunMap[row.Town].distance = row.Distance; |
|
return row; |
|
}; |
|
|
|
function dataLoaded(error, mapData, drivingTimes, racesRun){ |
|
function redraw(){ |
|
|
|
// Extract the width and height that was computed by CSS. |
|
const width = chartDiv.clientWidth; |
|
const height = chartDiv.clientHeight; |
|
const centerX = width/2; |
|
const centerY = height/2; |
|
|
|
// Use the extracted size to set the size of an SVG element. |
|
svg |
|
.attr("width", width) |
|
.attr("height", height); |
|
|
|
// Start work on the choropleth map |
|
// idea from https://www.youtube.com/watch?v=lJgEx_yb4u0&t=23s |
|
const mapScale = getMapScale(width, height); |
|
|
|
const CT_coords = [-72.7,41.6032]; |
|
const projection = d3.geoMercator() |
|
.center(CT_coords) |
|
.scale(mapScale) |
|
.translate([centerX, centerY]); |
|
const path = d3.geoPath().projection(projection); |
|
|
|
const group = svg.selectAll("g") |
|
//.data(mapData.features); |
|
.data(topojson.feature(mapData, mapData.objects.townct_37800_0000_2010_s100_census_1_shp_wgs84).features); |
|
|
|
const areas = group |
|
.enter() |
|
.append("g").append("path") |
|
.attr("d", path) |
|
.attr("class", d => d.properties.NAME10 in racesRunMap ? "area alreadyRun" : "area") |
|
.attr("fill", "steelblue") |
|
.append("title") |
|
.text(d => d.properties.NAME10 + ": " + drivingTimesMap[d.properties.NAME10].timeString); |
|
|
|
areas.merge(group).selectAll("path") |
|
.attr("d", path); |
|
} |
|
|
|
// Draw for the first time to initialize. |
|
redraw(); |
|
|
|
// Redraw based on the new size whenever the browser window is resized. |
|
window.addEventListener("resize", redraw); |
|
} |
|
|
|
d3.queue() |
|
.defer(d3.json, "ct_towns_simplified.topojson") |
|
.defer(d3.csv, "driving_times_from_avon.csv", build_driving_map) |
|
.defer(d3.csv, "towns_run.csv", build_races_run_map) |
|
.await(dataLoaded); |
|
|
|
|
|
</script> |
|
</body> |
|
</html> |