D3
OG
Old school D3 from simpler times
All examples
By author
By category
About
kristin-henry-sf
Full window
Github gist
Film Locations in San Francisco
<!DOCTYPE html> <meta charset="utf-8"> <style> /*body { background-color: #333; }*/ .axis path, .axis line { fill: none; stroke: #000; shape-rendering: crispEdges; } .axis text { font: 10px sans-serif; } .cells path { fill: none; pointer-events: all; } </style> <svg width="960" height="620"></svg> <script src="https://d3js.org/d3.v4.min.js"></script> <script> // data source: https://data.sfgov.org/Culture-and-Recreation/Film-Locations-in-San-Francisco/yitu-d5am/data // make sure to use %Y instead of %y, if have full year and not just last 2 nums of year var parseTime = d3.timeParse("%m/%d/%Y"); // input: 3/2/2017 var formatTime = d3.timeFormat("%m/%d/%y"); // output: 3/2/17 var svg = d3.select("svg"), margin = {top: 40, right: 40, bottom: 40, left: 40}, width = svg.attr("width") - margin.left - margin.right, height = svg.attr("height") - margin.top - margin.bottom; var x = d3.scaleLinear() .rangeRound([0, width]); var subsets = [] var g = svg.append("g") .attr("transform", "translate(" + margin.left + "," + margin.top + ")"); d3.csv('Film_Locations_in_San_Francisco.csv', function(error, data){ if (error) throw error; var films = d3.set(data, function(d){ return d["Title"]}); var filmNames = d3.values(films); var prod = d3.set(data, function(d){ return d["Production Company"]}); var distr = d3.set(data, function(d){ return d["Distributor"]}) var loc = d3.set(data, function(d){ return d["Locations"]}) locNames = d3.values(loc); // I might want to limit this to top 10? locGroups = []; for(var i=0; i<locNames.length; i++){ locGroups.push([]) } // filter out records that don't have category labels data.forEach(function(d){ d.year = d["Release Year"]; var i = locNames.indexOf(d["Locations"]) if(i >= 0){ locGroups[i].push(d) } }) var minCount = 5; //2; //1; // only draw subsets that have a minimum size for(var i=0; i<locGroups.length; i++){ if(locGroups[i].length > minCount){ subsets.push(locGroups[i]) } } drawChart(subsets); }); function getY(rows, row){ return (height / (rows+1)) * row; } function makeSim(data,rows, row){ var simulation = d3.forceSimulation(data) .force("x", d3.forceX(function(d) { return x(d.year); }).strength(1)) .force("y", d3.forceY( function(d){return getY(rows, row)})) .force("collide", d3.forceCollide(4)) .stop(); for (var i = 0; i < 120; ++i) simulation.tick(); } function makeVoronoi(data){ var cell = g.append("g") .attr("class", "cells") .selectAll("g").data(d3.voronoi() .extent([[-margin.left, -margin.top], [width + margin.right, height + margin.top]]) .x(function(d) { return d.x; }) .y(function(d) { return d.y; }) .polygons(data)).enter().append("g"); return cell; } function drawGuide(rows,i){ var guide = g.append("g") guide.append("line") .style("stroke", "#CCC") .style("stroke-width", .2) .attr("x1", 0) .attr("y1", getY(rows, i+1)) .attr("x2", width) .attr("y2", getY(rows, i+1)) guide.append("text") .attr("x", function(d) { return 0; }) .attr("y", function(d) { return getY(rows,i+1); }) .text(function(d){ var loc = subsets[i][0]["Locations"]; return loc != "" ? loc: "unspecified" }) .attr("font-family", "sans-serif") .attr("font-size", "12px") .attr("fill", "#CCC"); } function getFill(d){ return "black" } function getOpacity(d){ return .3 } function drawChart(subsets){ var rows = subsets.length; // collect all the subsets into a single list, so we can draw them var data = []; for(var i=0; i<rows; i++){ data = data.concat(subsets[i]) } // get min/max of all data, so we can use same axess x.domain(d3.extent(data, function(d) { return d.year; })); var xAxis = d3.axisBottom(x).tickFormat(d3.format("d")).tickSizeOuter(0); // this formats as year "1990", not "1,990" // group them into smal multiples for(var i=0; i<rows; i++){ makeSim(subsets[i], rows, i+1) // calculate positions within groups drawGuide(rows, i) // add annotation/guideline for group } // draw x axis, for dates g.append("g") .attr("class", "axis axis--x") .attr("transform", "translate(0," + height + ")") .call(xAxis) // create voronoi for selecting nearest dot with mouse/touch cell = makeVoronoi(data) // draw dots cell.append("circle") .attr("r", 2.4) .attr("cx", function(d) { return d.data.x; }) .attr("cy", function(d) { return d.data.y; }) .style("opacity", function(d){ return getOpacity(d); }) g.selectAll(".cells").selectAll("g") .on("mouseover",function(d,i){ // console.log("hello") d3.select(this) .style("fill","red"); }) .on("mouseout",function(d,i){ d3.select(this) .transition() .duration(500) .style("fill","black");//it is style }); cell.append("path") // .attr("d", function(d) { return "M" + d.join("L") + "Z"; }); // Tool Tip info cell.append("title") .text(function(d) { var str = d.data["Release Year"] + "\n" + d.data["Title"] + "\n" + d.data["Locations"] + "\n"; return str; }); }; function type(d) { if (!d.value) return; d.value = +d.value; return d; } </script>
https://d3js.org/d3.v4.min.js