Skip to content

Instantly share code, notes, and snippets.

@JenHLab
Created February 29, 2016 17:48
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save JenHLab/fabb713c310663d7e60f to your computer and use it in GitHub Desktop.
Save JenHLab/fabb713c310663d7e60f to your computer and use it in GitHub Desktop.
Dots on Lines
<!DOCTYPE html>
<!-- Modification of an example by Scott Murray from Knight D3 course -->
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="x-ua-compatible" content="ie=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Theme Park Attendance</title>
<link href='https://fonts.googleapis.com/css?family=Droid+Serif:400,700italic|Arapey|PT+Serif' rel='stylesheet' type='text/css'>
<style type="text/css">
body {
background-color: white;
font-family: 'PT Serif', serif;
box-shadow: 0 2px 10px 0 rgba(0, 0, 0, 1);
padding: 1.5rem;
margin-top: 3.5rem;
margin-bottom: 3.5rem;
}
h1 {
font-family: 'Droid Serif', serif;
font-size: 30px;
margin: 20px;
}
h3{
margin: 10px;
font-size: 18px;
margin: 20px;
width: inherit;
}
p {
font-size: 16px;
margin: 10px;
text-align: left;
margin: 20px;
}
a{
color: #0365d4;
}
svg {
background-color: white;
margin: 20px;
}
.axis path,
.axis line {
fill: none;
stroke: #0365d4;
stroke-width: 2px;
}
.line {
fill: none;
stroke: #b3d0f2;
stroke-width: 1px;
stroke-opacity: 80%;
}
.line.unfocused{
stroke-opacity: 40%;
}
.line.focused {
stroke-width: 2px;
stroke-opacity: 100%;
stroke: #0365d4;
}
.axis text {
font-family: sans-serif;
font-size: 11px;
}
.tooltip {
position: absolute;
z-index: 10;
}
.tooltip p {
background-color: white;
opacity: 40%;
color: #c60bdd;
border: none;
padding: 2px;
}
.voronoi path {
stroke: none;
fill: none;
pointer-events: all;
}
text.linelabel {
font-size: 9pt;
color: gray;
}
circle {
fill: #c60bdd;
}
.callout-card {
background: white;
border: 1px solid #b3d0f2;
box-shadow: 0 10px 20px rgba(0, 0, 0, 0.2);
margin: 1rem 0;
float: right;
width: 40%;
overflow: hidden;
}
.callout-card.warning .card-label {
border-color: transparent #b3d0f2 transparent transparent;
border-color: rgba(255, 255, 255, 0) #b3d0f2 rgba(255, 255, 255, 0) rgba(255, 255, 255, 0);
}
.callout-card .card-label {
border-style: solid;
border-width: 0 70px 40px 0;
float: right;
height: 0px;
width: 0px;
transform: rotate(360deg);
}
.callout-card .callout-card-content {
padding: 0.5rem 1.5rem 0.875rem;
}
.callout-card.radius {
border-radius: 0.6rem;
}
</style>
</head>
<body>
<h1>Walt Disney Co. Parks Continues to Welcome Over 100 Million Visitors Yearly</h1>
<h3>by <span><i>Jennifer Hernandez</i></span></h3>
<br>
<p>Walt Disney Co. continues leading the amusement and theme park industry with the most number of visitors to their parks annually. In 2014, the Themed Entertainment Association (TEA) reported that Walt Disney Co. received 134,300,000 million visitors while its major competitor in the U.S., Universal Studios Recretion Group, had 40,152,000 visitors that same year.
<br><br>
The Walt Disney Co. Parks and Resorts business unit operates 9 of the top 10 most visited parks in the world. Its most popular park is Magic Kingdom at Walt Disney World, Orlando, Florida with 19,332,000 million visitors in 2014, followed by Tokyo Disneyland, Disneyland at Disneyland Resort and Tokyo DisneySea at Tokyo Disney Resort.
<br><br>
Universal Studios Recretion Group has been experiencing a steady increase in the number of visitors to Universal Studios Florida at Universal Studios Orlando Resort since the opening of the Wizarding World of Harry Potter. In 2014, over 8.2 million people visited the park, 1 million more people from the previous year.
</p>
<div class="small-12 medium-6 columns">
<div class="callout-card warning radius">
<div class="callout-card-content">
<h3 class="lead" style="color:#0365d4;">Top 10 Most Visited Parks in 2014</h3>
<table>
<thead>
<tr style="font-size: 15px;">
<th width="350">Theme Park</th>
<th width="150">Visitors (Million)</th>
</tr>
</thead>
<tbody style="font-size: 13px;">
<tr>
<td>Magic Kingdom at Walt Disney World Resort</td>
<td>19,332,000</td>
</tr>
<tr>
<td>Tokyo Disneyland at Tokyo Disney Resort</td>
<td>17,300,000</td>
</tr>
<tr>
<td>Disneyland at Disneyland Resort</td>
<td>16,769,000</td>
</tr>
<tr>
<td>Tokyo DisneySea at Tokyo Disney Resort</td>
<td>14,100,000</td>
</tr>
<tr>
<td>Universal Studios Japan</td>
<td>11,800,000</td>
</tr>
<tr>
<td>Epcot at Walt Disney World Resort</td>
<td>11,454,000</td>
</tr>
<tr>
<td>Disney's Animal Kingdom at Walt Disney World Resort</td>
<td>10,402,000</td>
</tr>
<tr>
<td>Disney's Hollywood Studios at Walt Disney World Resort</td>
<td>10,312,000</td>
</tr>
<tr>
<td>Disneyland Park at Disneyland Paris</td>
<td>9,940,000</td>
</tr>
<tr>
<td>Disney California Adventure at Disneyland Resort</td>
<td>8,769,000</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</div>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.6/d3.min.js"></script>
<script type="text/javascript">
//Dimensions and padding
var fullwidth = 700;
var fullheight = 600;
var margin = {top: 20, right: 220, bottom: 40, left:100};
var width = fullwidth - margin.left - margin.right;
var height = fullheight - margin.top - margin.bottom;
//Set up date formatting and years
var dateFormat = d3.time.format("%Y");
//Set up scales
var xScale = d3.time.scale()
.range([ 0, width ]);
var yScale = d3.scale.linear()
.range([10, height ]);
//Configure axis generators
var xAxis = d3.svg.axis()
.scale(xScale)
.orient("bottom")
.ticks(8)
.tickFormat(function(d) {
return dateFormat(d);
})
.outerTickSize([0])
.innerTickSize([5]);
var yAxis = d3.svg.axis()
.scale(yScale)
.orient("left")
.outerTickSize([0]);
//Configure line generator
// each line dataset must have a d.year and a d.amount for this to work.
var line = d3.svg.line()
.x(function(d) {
return xScale(dateFormat.parse(d.year));
})
.y(function(d) {
return yScale(+d.amount);
});
//Create the empty SVG image
var svg = d3.select("body")
.append("svg")
.attr("width", fullwidth)
.attr("height", fullheight)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
var tooltip = d3.select("body")
.append("div")
.attr("class", "tooltip");
//Load data
d3.csv("pAttendance.csv", function(data) {
//New array with all the years, for referencing later
var years = ["2007", "2008", "2009", "2010", "2011", "2012", "2013", "2014"];
//Create a new, empty array to hold our restructured dataset
var dataset = [];
//Loop once for each row in data
data.forEach(function (d, i) {
var myAttendance = [];
//Loop through all the years - and get the emissions for this data element
years.forEach(function (y) {
// If value is not empty
if (d[y]) {
myAttendance.push({
park: d.amusementPark,
year: y,
amount: d[y] // this is the value for, for example, d["2004"]
});
}
});
dataset.push( {
park: d.amusementPark,
attendance: myAttendance
} );
});
//Uncomment to log the original data to the console
// console.log(data);
//Uncomment to log the newly restructured dataset to the console
console.log(dataset);
//Set scale domains - max and mine of the years
xScale.domain(
d3.extent(years, function(d) {
return dateFormat.parse(d);
}));
// max of emissions to 0 (reversed, remember)
yScale.domain([
d3.max(dataset, function(d) {
return d3.max(d.attendance, function(d) {
return +d.amount;
});
}),
2000000
]);
//Make a group for each country
var groups = svg.selectAll("g")
.data(dataset)
.enter()
.append("g")
.attr("class", "park");
//Within each group, create a new line/path,
groups
.append("path")
.attr("class", "line")
.attr("d", function(d) {
console.log(d, this);
d.line = this;
return line(d.attendance);
});
groups.each(function(d) {
console.log(d);
})
// Adding a subtle animation to increase the dot size when over it!
var voronoi = d3.geom.voronoi()
.x(function(d){
return xScale(dateFormat.parse(d.year));
})
.y(function(d) {
return yScale(+d.amount);
})
.clipExtent([[margin.left - 120, margin.top - 50], [width + 20, height + 20]]);
var voronoiGroup = svg.append("g")
.attr("class", "voronoi");
// we have to do this nesting trick because a lot of dots are in the same place.
// the goal here is to map to single points -- no dupes at the same point -- and after
// the nesting, return just the actual values (not the keys).
// The values in a nest are the original items that went into each key.
var nested = d3.nest()
.key(function(d) { // this key is the pair of x/y coords - to keep them single
return xScale(dateFormat.parse(d.year)) + "," + yScale(+d.amount);
})
.rollup(function(v) {
return v[0]; // first of the objects, prevents use of 2 in same place
})
.entries(d3.merge(dataset.map(function(d) { return d.attendance; })))
.map(function (d) { return d.values;}); // the result is just a flat list of values
console.log(nested);
voronoiGroup.selectAll("path")
.data(voronoi(nested))
.enter()
.append("path")
.attr("d", function(d, i) {
return "M" + d.join("L") + "Z"; }) //copy for a voronoi
.datum(function(d, i) { return d.point; })
.on("mouseover", mouseoverFunc) // mouseover goes on the voronoi cell!
.on("mousemove", mousemoveFunc)
.on("mouseout", mouseoutFunc);
// We're putting the text label at the group level
groups.append("text")
.attr("x", function(d) {
if (d.attendance.length != 0) {
var lastYear = d.attendance[d.attendance.length-1].year;
return xScale(dateFormat.parse(lastYear));
}
})
.attr("y", function(d) {
if (d.attendance.length != 0) {
var lastAmount = d.attendance[d.attendance.length-1].amount;
return yScale(+lastAmount);
}
})
.attr("dx", "3px")
.attr("dy", "3px")
.text(function(d) {
if (d.attendance.length != 0) {
var lastAmount = d.attendance[d.attendance.length-1].amount;
if (+lastAmount > 11000000) {
return d.park;
}
}
})
.attr("class", "linelabel");
// the line's dot is the "focus" element
var focus = svg.append("g")
.attr("transform", "translate(-100,-100)") // it's off screen
.attr("class", "focus");
focus.append("circle")
.attr("r", 3.5);
focus.append("text")
.attr("y", -10);
//Title
svg.append("text")
.attr("x", (width - 400))
.attr("y", 7 - (margin.top / 2))
.style("font-weight", "bold")
.style("font-size", "19px")
.style("fill", "#0365d4")
.text("Theme Park Visitors 2007-2014");
//Axes
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
svg.append("g")
.attr("class", "y axis")
.call(yAxis);
function mouseoverFunc(d) {
// position the dot
focus.attr("transform", "translate(" + xScale(dateFormat.parse(d.year)) + "," + yScale(+d.amount) + ")");
// show the tooltip
tooltip
.style("display", null) // this removes the display none setting from it
.html("<p>Park: " + d.park +
"<br>Year: " + d.year +
"<br>Attendance: " + d.amount + " visitors</p>");
}
function mousemoveFunc(d) {
tooltip
.style("top", (d3.event.pageY - 10) + "px" )
.style("left", (d3.event.pageX + 10) + "px");
}
function mouseoutFunc(d) {
// remove the dot offscreen and the tooltip
focus.attr("transform", "translate(-100,-100)");
d3.selectAll("path.line").classed("focused", false);
tooltip.style("display", "none"); // this sets it to invisible!
}
}); // end of data csv
</script>
<p style="font-size: 13px;"><b>Sources:</b>
<br>
<i><a href="https://cdn.thewaltdisneycompany.com/sites/default/files/reports/q4-fy15-earnings.pdf">The Walt Disney Company Reports: Fourth Quater and Full Year Earnings for Fiscal 2015</a></i>. The Walt Disney Company. 2015.
<br>
<br>
<i><a href="http://www.teaconnect.org/images/files/TEA_103_49736_150603.pdf">TEA/AECOM 2014 Theme Index and Museum Index: The Global Attendance Report</a></i>. Themed Entertainment Association (TEA). 2015.
</p>
</body>
</html>
amusementPark 2007 2008 2009 2010 2011 2012 2013 2014
Magic Kingdom at Walt Disney World Resort 16640000 17063000 17233000 16972000 17142000 17536000 18588000 19332000
Tokyo Disneyland at Tokyo Disney Resort 12900000 14293000 13646000 14452000 13996000 14847000 17214000 17300000
Disneyland at Disneyland Resort 14730000 14721000 15900000 15980000 16140000 15963000 16202000 16769000
Tokyo DisneySea at Tokyo Disney Resort 12100000 12498000 12004000 12663000 11930000 12656000 14084000 14100000
Universal Studios Japan 8500000 8300000 8000000 8160000 8500000 9700000 10100000 11800000
Epcot at Walt Disney World Resort 10460000 10935000 10990000 10825000 10825000 11063000 11229000 11454000
Disney's Animal Kingdom at Walt Disney World Resort 9100000 9540000 9590000 9686000 9783000 9998000 10198000 10402000
Disney's Hollywood Studios at Walt Disney World Resort 8910000 9608000 9700000 9603000 9699000 9912000 10110000 10312000
Disneyland Park at Disneyland Paris 10600000 12688000 12740000 10500000 10990000 11200000 10430000 9940000
Disney California Adventure at Disneyland Resort 5950000 5566000 6095000 6287000 6341000 7775000 8514000 8769000
Universal Studios Florida at Universal Orlando Resort 6000000 6231000 5530000 5925000 6044000 6195000 7062000 8263000
Islands of Adventure at Universal Orlando Resort 5300000 5297000 4627000 5949000 7674000 7981000 8141000 8141000
Ocean Park 4380000 5030000 4800000 5404000 6955000 7436000 7475000 7792000
Lotte World 5500000 4236000 4261000 5551000 5780000 6383000 7400000 7606000
Hong Kong Disneyland at Hong Kong Disneyland Resort 5200000 4500000 4600000 5200000 5900000 6700000 7400000 7500000
Everland 7500000 6600000 6169000 6884000 6570000 6853000 7303000 7381000
Universal Studios Hollywood 4700000 4583000 4308000 5040000 5141000 5912000 6148000 6824000
Songcheng Park 3797000 3327000 3800000 4100000 5810000
Nagashima Spa Land 3910000 3734000 4700000 4465000 5820000 5850000 5840000 5630000
Chimelong Ocean Kingdom 5504000
Europa Park 3950000 4000000 4250000 4250000 4500000 4600000 4900000 5000000
SeaWorld Orlando 5740000 5926000 5800000 5100000 5202000 5358000 5090000 4683000
Tivoli Gardens 4396000 3972000 3870000 3696000 3963000 4033000 4200000 4478000
Efteling 3200000 3200000 4000000 4000000 4125000 4200000 4150000 4400000
Walt Disney Studios Park at Disneyland Paris 2200000 2612000 2655000 4500000 4710000 4800000 4470000 4260000
Busch Gardens Tampa Bay 4360000 4410000 4100000 4200000 4284000 4348000 4087000 4128000
Universal Studios Singapore 2000000 3411000 3480000 3650000 3840000
SeaWorld San Diego 4260000 4174000 4200000 3800000 4294000 4444000 4311000 3794000
OCT East 3530000 3890000 4196000 3950000 3780000
Changzhou Dinosaur Park 2300000 3500000 3400000 3600000 3700000
Knott's Berry Farm 3670000 3565000 3333000 3600000 3654000 3508000 3683000 3683000
Window of the World 2500000 2651000 3123000 3170000 3250000 3600000
Canada's Wonderland 3230000 3380000 3160000 3380000 3481000 3655000 3582000 3546000
PortAventura 3500000 3300000 3000000 3050000 3522000 3540000 3400000 3500000
Chimelong Paradise 2400000 2400000 2700000 2970000 3200000 3351000
Happy Valley 2350000 2734000 3438000 3055000 3100000 3340000
Happy Valley 2930000 3180000 2800000 3050000 3890000 3212000 3282000 3300000
Cedar Point 3070000 3198000 2942000 3051000 3143000 3221000 3382000 3247000
Kings Island 3050000 3126000 3000000 3112000 3143000 3206000 3206000 3238000
Hersheypark 2690000 2842000 2807000 2891000 2949000 3140000 3180000 3212000
Liseberg 2950000 3050000 3150000 2900000 2900000 2800000 2860000 3100000
Six Flags Magic Mountain 2550000 2500000 2600000 2700000 2808000 2906000 2848000
Six Flags Great Adventure 2730000 2761000 2634000 2700000 2650000 2800000 2800000
Gardaland 3100000 3100000 2900000 2800000 2850000 2700000 2700000 2750000
Busch Gardens Williamsburg 3094000 2900000 2800000 2744000 2854000 2726000 2699000
Alton Towers 2400000 2520000 2650000 3000000 2750000 2400000 2500000 2575000
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment