'use strict'; var svg = d3.select('svg'); svg.append('text').attr('class', 'title').attr('x', 960 / 2).attr('y', 35).text("Farmers' Markets Goods Comparison"); d3.json('https://gist.githubusercontent.com/susielu/3d194b8660ec6ab214a3/raw/38a2cdc96efaaaeb4849c86b600de5dfecea2dec/farmers-markets-lat-long.json', function (error, data) { var h = 480; var w = 800; var padding = 20; var xScale = d3.scaleLinear().range([padding, w - padding]).domain([-130, -65]); var xBarScale = d3.scaleLinear().range([padding, w - padding]); var yScale = d3.scaleLinear().range([h - padding, padding]).domain([20, 50]); var comp1 = "maple"; var comp2 = "seafood"; var selected = 'comp1'; var offset = 'translate(60, 40)'; //Filtering out states outside of the contiguous US for simplicity data = data.filter(function (d) { return d.x >= -130 && d.x <= -65 && d.y >= 20 && d.y <= 50; }); //Making a legend w00t http://d3-legend.susielu.com/ var colors = d3.scaleOrdinal().domain(['' + comp1, '' + comp2, 'both']).range(["rgba(0, 200, 200, .5)", "rgba(200, 0, 200, .5)", "#ac8cdc"]); var colorLegend = d3.legendColor().shapeHeight(8).shapePadding(5).scale(colors); svg.append('g').attr('class', 'legend').attr('transform', 'translate(200, 390)').call(colorLegend); var map = svg.append('g').attr('class', 'map').attr('transform', offset); map.selectAll('circle').data(data).enter().append('circle').attr('r', 1).attr('cx', function (d) { return xScale(d.x); }).attr('cy', function (d) { return yScale(d.y); }); var rollup = function rollup(leaves) { var first = 0; var second = 0; var both = 0; leaves.forEach(function (l) { if (l[comp1] === "Y") { first++; } if (l[comp2] === "Y") { second++; } if (l[comp1] === "Y" && l[comp2] === "Y") { both++; } }); return { length: leaves.length, comp1: first, comp2: second, both: both }; }; var lat = svg.append('g').attr('class', 'lat').attr('transform', offset); var latArea = d3.area().x(function (d) { return xScale(parseInt(d.key)); }).y1(function (d) { return yLatScale(d.value.length); }).y0(function (d) { return yLatScale(0); }).curve(d3.curveCardinal), latNested = d3.nest().key(function (d) { return Math.round(d.x); }).rollup(rollup).entries(data).sort(function (a, b) { return parseInt(a.key) - parseInt(b.key); }); var yLatMax = d3.max(latNested, function (d) { return d.value.length; }); var yLatScale = d3.scaleLinear().range([h - 40, h - 140]).domain([0, yLatMax]); //Makes a horizontal bar chart then rotates it for the longitudinal graph var long = svg.append('g').attr('class', 'long').attr('transform', 'rotate(90, ' + (w + 60) + ', 40) ' + offset); var xLongScale = d3.scaleLinear().range([w + padding, w + h - padding]).domain([50, 20]); var longArea = d3.area().x(function (d) { return xLongScale(parseInt(d.key)); }).y1(function (d) { return yLongScale(d.value.length); }).y0(function (d) { return yLongScale(0); }).curve(d3.curveCardinal), longNested = d3.nest().key(function (d) { return Math.round(d.y); }).rollup(rollup).entries(data).sort(function (a, b) { return parseInt(a.key) - parseInt(b.key); }); var yLongMax = d3.max(longNested, function (d) { return d.value.length; }); var yLongScale = d3.scaleLinear().range([padding, padding - 100]).domain([0, yLongMax]); var transition = d3.transition().ease(d3.easePolyInOut); var createHistogram = function createHistogram(group, area, nest) { group.append('path').attr('fill', 'none').attr('stroke', 'grey').attr('d', area(nest)); group.append('path').attr('class', 'comp1'); group.append('path').attr('class', 'comp2'); }; var updateMap = function updateMap() { map.selectAll('circle').attr('class', function (d) { return d[comp1] === "Y" && d[comp2] === "Y" ? 'compBoth' : d[comp1] === "Y" ? 'comp1' : d[comp2] === "Y" ? 'comp2' : ''; }); }; var updateHistogram = function updateHistogram(type, group, area, nest, scale) { var nestKey = type === "lat" ? 'x' : 'y'; nest = d3.nest().key(function (d) { return Math.round(d[nestKey]); }).rollup(rollup).entries(data).sort(function (a, b) { return parseInt(a.key) - parseInt(b.key); }); //Overlapping bump area logic area.y1(function (d) { if (d.value.comp1 > d.value.comp2) { return scale(d.value.comp1); } else { return scale(d.value.comp1 + d.value.comp2 - d.value.both); } }); area.y0(function (d) { if (d.value.comp1 > d.value.comp2) { return scale(0); } else { return scale(d.value.comp2 - d.value.both); } }); group.select('path.comp1').transition(transition).attr('d', area(nest)); //Overlapping bump area logic area.y1(function (d) { if (d.value.comp2 > d.value.comp1) { return scale(d.value.comp2); } else { return scale(d.value.comp1 + d.value.comp2 - d.value.both); } }); area.y0(function (d) { if (d.value.comp2 > d.value.comp1) { return scale(0); } else { return scale(d.value.comp1 - d.value.both); } }); group.select('path.comp2').transition(transition).attr('d', area(nest)); }; var update = function update() { updateMap(); updateHistogram('lat', lat, latArea, latNested, yLatScale); updateHistogram('long', long, longArea, longNested, yLongScale); //Update text colors in Goods selector svg.selectAll('.types text').attr('class', function (d) { return d.key === comp1 ? 'comp1' : d.key === comp2 ? 'comp2' : ''; }); //Update legend key colors.domain(['' + comp1, '' + comp2, 'both']); colorLegend.scale(colors); svg.select('g.legend').call(colorLegend); }; //Initial render of graphs and map createHistogram(lat, latArea, latNested); createHistogram(long, longArea, longNested); update(); var variables = [{ "key": "vegetables", "label": "Vegetables 96%", "percent": .96 }, { "key": "bakedgoods", "label": "Baked Goods 88%", "percent": .88 }, { "key": "honey", "label": "Honey 81%", "percent": .81 }, { "key": "jams", "label": "Jams 80%", "percent": .80 }, { "key": "fruits", "label": "Fruits 80%", "percent": .80 }, { "key": "herbs", "label": "Herbs 79%", "percent": .79 }, { "key": "eggs", "label": "Eggs 74%", "percent": .74 }, { "key": "flower", "label": "Flowers 69%", "percent": .69 }, { "key": "soap", "label": "Soap 67%", "percent": .67 }, { "key": "plants", "label": "Plants 66%", "percent": .66 }, { "key": "crafts", "label": "Crafts 61%", "percent": .61 }, { "key": "prepared", "label": "Prepared Food 61%", "percent": .61 }, { "key": "meat", "label": "Meat 55%", "percent": .55 }, { "key": "cheese", "label": "Cheese 50%", "percent": .50 }, { "key": "poultry", "label": "Poultry 45%", "percent": .45 }, { "key": "coffee", "label": "Coffee 33%", "percent": .33 }, { "key": "maple", "label": "Maple 32%", "percent": .32 }, { "key": "nuts", "label": "Nuts 29%", "percent": .29 }, { "key": "trees", "label": "Trees 29%", "percent": .29 }, { "key": "seafood", "label": "Seafood 24%", "percent": .24 }, { "key": "juices", "label": "Juices 22%", "percent": .22 }, { "key": "mushrooms", "label": "Mushrooms 22%", "percent": .22 }, { "key": "petfood", "label": "Pet Food 18%", "percent": .18 }, { "key": "wine", "label": "Wine 17%", "percent": .17 }, { "key": "beans", "label": "Beans 14%", "percent": .14 }, { "key": "grains", "label": "Grains 14%", "percent": .14 }, { "key": "wildharvest", "label": "Wild Harvest 13%", "percent": .13 }, { "key": "nursery", "label": "Nursery 6%", "percent": .06 }, { "key": "tofu", "label": "Tofu 4%", "percent": .04 }]; svg.append('text').attr('class', '.controlTitle').attr('x', 20).attr('y', 40).text('Goods selector'); svg.selectAll('rect.control').data(['comp1', 'comp2']).enter().append('rect').attr('x', function (d, i) { return 20 + i * 20; }).attr('y', 50).attr('width', 15).attr('height', 15).attr('class', function (d) { return 'control ' + d + ' ' + (selected === d ? 'selected' : ''); }).on('click', function (d) { if (selected === "comp1") { selected = "comp2"; } else { selected = "comp1"; } svg.selectAll('rect.control').attr('class', function (d) { return 'control ' + d + ' ' + (selected === d ? 'selected' : ''); }); }); var types = svg.append('g').attr('class', 'types'); var changeComp = function changeComp(d) { if (selected === "comp1") { comp1 = d.key; } else { comp2 = d.key; } update(); }; types.selectAll('text').data(variables).enter().append('text').attr('x', 20).attr('y', function (d, i) { return i * 14 + 80; }).text(function (d) { return d.label; }).attr('class', function (d) { return d.key === comp1 ? 'comp1' : d.key === comp2 ? 'comp2' : ''; }).on('click', changeComp); });