From 5748bf9f3e7cfae7949f96fcf7369a11a3d6fe11 Mon Sep 17 00:00:00 2001 From: LauraKirby Date: Thu, 6 Jul 2017 15:58:34 -0700 Subject: [PATCH] make map responsive, add title to map using d3 'text' function, update Readme to reflect the work completed. --- index.html | 22 +++--- workshop-map.css | 7 ++ workshop-map.js | 188 +++++++++++++++++++++++++++-------------------- 3 files changed, 129 insertions(+), 88 deletions(-) diff --git a/index.html b/index.html index 59d73b0..56e00ff 100644 --- a/index.html +++ b/index.html @@ -1,21 +1,23 @@ + Bridge Foundry workshop locations - -

Bridge Foundry workshop locations

- - - + + + + -
- - +
+ + + + diff --git a/workshop-map.css b/workshop-map.css index f27d188..51972ab 100644 --- a/workshop-map.css +++ b/workshop-map.css @@ -21,4 +21,11 @@ div.tooltip { div.hide { display: none; +} + +#chart { + margin:10%; + border:2px solid #d0d0d0; + border-radius: 5px; + z-index: 10 } \ No newline at end of file diff --git a/workshop-map.js b/workshop-map.js index aa027fd..f69b587 100644 --- a/workshop-map.js +++ b/workshop-map.js @@ -1,101 +1,133 @@ function workshopMap() { - var width = 960, - height = 450; + var svg, container, chartWrapper, path, width, height; + + width = 960, height = 450; + + //initialize svg + svg = d3.select('#chart').append('svg') + .attr("width", width) + .attr("height", height) + .call(responsivefy) + function chart(selection) { selection.each(function(data) { - var projection = d3.geoMercator(); + var projection = d3.geoMercator() + // initial size of map + .scale(150) + .translate([width / 2, height / 2]); var path = d3.geoPath() .projection(projection); - - var svg = d3.select(this).append("svg") - .attr("width", width) - .attr("height", height); - var tooltipDiv = d3.select("body").append("div") .attr("class", "tooltip hide") - // source: https://docs.google.com/spreadsheet/pub?key=0AjPWVMj9wWa6dDJOVE5DVTRxbjc2Vy1PMVlQTlh4eFE&single=true&gid=0&output=csv - // google spreadsheets no longer allows cross-domain access + //load data, then initialize chart d3.queue() - .defer(d3.json, "data/world-50m.json") - .defer(d3.csv, "data/workshop-data.csv") - .await(ready); + .defer(d3.json, "data/world-50m.json") + .defer(d3.csv, "data/workshop-data.csv") + .await(init); // load and display the World - function ready(error, world, workshops) { + function init(error, world, workshops) { if (error) return console.log("there was an error loading the data: " + error); - var zoom, - circles; - - const g = svg.append("g"); + var zoom, circles; - g.append("path") + chartWrapper = svg.append('g') + .append("path") .datum(topojson.feature(world, world.objects.countries)) - .attr("d", path); - - - - circles = svg.selectAll("circle") - .data(workshops) - .enter().append("circle") - .attr("class", "symbol") - .attr("cx", d => projection([d.longitude, d.latitude])[0]) - .attr("cy", d => projection([d.longitude, d.latitude])[1]) - .attr("r", d => 5) - .on("mouseover", function(d) { - var html = `${d.city}
`; - //if (d["number (2013?)"]) { html += `${d["number (2013?)"]} Workshop`; } - //if (+d["number (2013?)"] > 1) { html += "s"; } - tooltipDiv.html(html); - tooltipDiv.style("opacity", 0); - tooltipDiv.attr("class", "tooltip"); - var width = +tooltipDiv.style("width").slice(0, -2); - var height = +tooltipDiv.style("height").slice(0, -2); - tooltipDiv.style("left", `${d3.event.pageX - (width / 2)}px`); - tooltipDiv.style("top", `${d3.event.pageY - height - 20}px`); - tooltipDiv.style("opacity",1); - console.log(`City: ${d.city}, # of Workshops: ${d["number (2013?)"]}`); - }) - .on("mouseout", d => { - tooltipDiv.attr("class", "tooltip hide"); - g.select("path").node().focus(); - }); - - zoom = d3.zoom().on("zoom", zoomed) - function zoomed(d) { - const transform = d3.event.transform; - const x = transform.x; - const y = transform.y; - const k = transform.k; - circles.transition().duration(100) - .attr("transform", `translate(${x},${y}) scale(${k})`) - .attr("r", k < 4 ? 5 - k + 0.1 : 5/k) - .attr("stroke-width", k < 2 ? .75 : 0); - - g.transition().duration(100) - .attr("transform", `translate(${x},${y}) scale(${k})`) - .attr("stroke-width", k < 2 ? 2 : 0.0001); - } - - g.select("path").node().focus(); + .attr("d", path) + + circles = drawCircles(svg, workshops, projection, tooltipDiv); + + var zoom = d3.zoom() + .on("zoom", zoomed); + svg.call(zoom); } }); } - chart.width = function(_) { - if (!arguments.length) return width; - width = _; - return chart; - }; - - chart.height = function(_) { - if (!arguments.length) return height; - height = _; - return chart; - }; + appendTitle(svg) + + //render the chart return chart; -} \ No newline at end of file +} + + +function zoomed() { + const transform = d3.event.transform; + const x = transform.x; + const y = transform.y; + const k = transform.k; + circles.transition().duration(100) + .attr("transform", `translate(${x},${y}) scale(${k})`) + .attr("r", k < 4 ? 5 - k + 0.1 : 5/k) + .attr("stroke-width", k < 2 ? .75 : 0); + + chartWrapper.transition().duration(100) + .attr("transform", `translate(${x},${y}) scale(${k})`) + .attr("stroke-width", k < 2 ? 2 : 0.0001); +} + +function drawCircles(svg, workshops, projection, tooltipDiv, chartWrapper){ + circles = svg.selectAll("circle") + .data(workshops) + .enter().append("circle") + .attr("class", "symbol") + .attr("cx", d => projection([d.longitude, d.latitude])[0]) + .attr("cy", d => projection([d.longitude, d.latitude])[1]) + .attr("r", d => 5) + .on("mouseover", function(d) { + var html = `${d.city}
`; + //if (d["number (2013?)"]) { html += `${d["number (2013?)"]} Workshop`; } + //if (+d["number (2013?)"] > 1) { html += "s"; } + tooltipDiv.html(html); + tooltipDiv.style("opacity", 0); + tooltipDiv.attr("class", "tooltip"); + var width = +tooltipDiv.style("width").slice(0, -2); + var height = +tooltipDiv.style("height").slice(0, -2); + tooltipDiv.style("left", `${d3.event.pageX - (width / 2)}px`); + tooltipDiv.style("top", `${d3.event.pageY - height - 20}px`); + tooltipDiv.style("opacity",1); + // console.log(`City: ${d.city}, # of Workshops: ${d["number (2013?)"]}`); + }) + .on("mouseout", d => { + tooltipDiv.attr("class", "tooltip hide"); + }); + return circles +} + +function responsivefy(svg) { + // get container + svg aspect ratio + container = d3.select(svg.node().parentNode), + width = parseInt(svg.style("width")), + height = parseInt(svg.style("height")), + aspect = width / height; + + svg.attr("viewBox", "0 0 " + width + " " + height) + .attr("perserveAspectRatio", "xMinYMid") + .call(resize); + + d3.select(window).on("resize", resize); + + // get width of container and resize svg to fit it + function resize() { + containerWidth = parseInt(container.style("width")); + var targetWidth = containerWidth?containerWidth:parseInt(svg.style("width")); + svg.attr("width", targetWidth); + svg.attr("height", Math.round(targetWidth / aspect)); + } +}; + +function appendTitle(svg){ + var padding = 100 + svg.append("text") + .attr("text-anchor", "middle") + .style("font-size", "20px") + .attr("transform", "translate("+ (width/2) +","+(height-(padding/10))+")") + .text("Bridge Foundry workshop locations"); +} + +