|
<!DOCTYPE html> |
|
<html> |
|
<head> |
|
<meta charset="utf-8"> |
|
<title>Spinning globe with glowing city markers in D3</title> |
|
<script src="//cdnjs.cloudflare.com/ajax/libs/d3/3.5.5/d3.min.js"></script> |
|
<link href="http://fonts.googleapis.com/css?family=Lora:700italic" rel="stylesheet" type="text/css"> |
|
<link href="http://fonts.googleapis.com/css?family=Muli" rel="stylesheet" type="text/css"> |
|
|
|
<style> |
|
|
|
body {background: black;} |
|
|
|
h1 { |
|
font: italic bold 2.2em Lora; |
|
color: #BBB; |
|
margin: 0px; |
|
} |
|
|
|
p { |
|
margin-top: 1.2em; |
|
margin-bottom: 0.3em; |
|
font: 1em Muli; |
|
color: #BBB; |
|
} |
|
|
|
controls { |
|
width:220px; |
|
position: absolute; |
|
padding:10px; |
|
} |
|
|
|
</style> |
|
</head> |
|
|
|
<body> |
|
|
|
<!-- initiate HTML5 sliders and color picker --> |
|
<controls> |
|
<h1> SPINNING GLOBE </h1> |
|
<p> Rotation speed & direction</p> |
|
<input id="rotation" type="range" min="-0.1" max="0.1" step="0.005" value="0.015" style="width: 160px"/> |
|
|
|
<p> Glow opacity</p> |
|
<input id="glow" type="range" min="0.05" max="0.5" step="0.01" value="0.3" style="width: 160px"/> |
|
|
|
<p> Marker size</p> |
|
<input id="marker_size" type="range" min="1" max="10" step="0.5" value="5" style="width: 160px"/> |
|
|
|
<p> Color </p> |
|
<input id="color" type="color" value="#ffba00"> |
|
</controls> |
|
|
|
<svg width="820" height="620"></svg> |
|
|
|
<script> |
|
// Map configuration |
|
var width = 820; |
|
var height = 620; |
|
var rScale = d3.scale.sqrt(); |
|
var peoplePerPixel = 50000; |
|
var max_population = []; |
|
|
|
// Configuration for the spinning effect |
|
var time = Date.now(); |
|
var rotate = [0, 0]; |
|
var velocity = [.015, -0]; |
|
|
|
// set projection type and paremetes |
|
var projection = d3.geo.orthographic() |
|
.scale(300) |
|
.translate([(width / 2) + 100, height / 2]) |
|
.clipAngle(90) |
|
.precision(0.3); |
|
|
|
// create path variable, empty svg element and group container |
|
var path = d3.geo.path() |
|
.projection(projection); |
|
var svg = d3.select("svg"); |
|
var g = svg.append("g"); |
|
|
|
// drawing dark grey spehere as landmass |
|
g.append("path") |
|
.datum({type: "Sphere"}) |
|
.attr("class", "sphere") |
|
.attr("d", path) |
|
.attr("fill", "#0D0D0D"); |
|
|
|
// loading city locations from geoJSON |
|
d3.json("geonames_cities_100k.geojson", function(error, data) { |
|
|
|
// Handle errors getting and parsing the data |
|
if (error) { return error; } |
|
|
|
// setting the circle size (not radius!) according to the number of inhabitants per city |
|
population_array = []; |
|
for (i = 0; i < data.features.length; i++) { |
|
population_array.push(data.features[i].properties.population); |
|
} |
|
max_population = population_array.sort(d3.descending)[0] |
|
var rMin = 0; |
|
var rMax = Math.sqrt(max_population / (peoplePerPixel * Math.PI)); |
|
rScale.domain([0, max_population]); |
|
rScale.range([rMin, rMax]); |
|
|
|
path.pointRadius(function(d) { |
|
return d.properties ? rScale(d.properties.population) : 1; |
|
|
|
}); |
|
|
|
// Drawing transparent circle markers for cities |
|
g.selectAll("path.cities").data(data.features) |
|
.enter().append("path") |
|
.attr("class", "cities") |
|
.attr("d", path) |
|
.attr("fill", "#ffba00") |
|
.attr("fill-opacity", 0.3); |
|
|
|
// start spinning! |
|
spinning_globe(); |
|
}); |
|
|
|
function spinning_globe(){ |
|
d3.timer(function() { |
|
|
|
// get current time |
|
var dt = Date.now() - time; |
|
|
|
// get the new position from modified projection function |
|
projection.rotate([rotate[0] + velocity[0] * dt, rotate[1] + velocity[1] * dt]); |
|
|
|
// update cities position = redraw |
|
svg.selectAll("path.cities").attr("d", path); |
|
}); |
|
|
|
} |
|
|
|
// Events for sliders and button |
|
document.getElementById("rotation").addEventListener("change", function() { |
|
var new_speed = this.value; |
|
velocity[0] = new_speed |
|
}); |
|
|
|
document.getElementById("glow").addEventListener("change", function() { |
|
var new_glow = this.value; |
|
g.selectAll("path.cities") |
|
.attr("fill-opacity", new_glow); |
|
}); |
|
|
|
document.getElementById("marker_size").addEventListener("change", function() { |
|
var new_marker_size = 1 / this.value ; |
|
peoplePerPixel = new_marker_size * 100000; |
|
var rMin = 0; |
|
var rMax = Math.sqrt(max_population / (peoplePerPixel * Math.PI)); |
|
rScale.range([rMin, rMax]); |
|
}); |
|
|
|
document.getElementById("color").addEventListener("change", function() { |
|
var new_color = this.value; |
|
g.selectAll("path.cities") |
|
.attr("fill", new_color); |
|
}); |
|
|
|
// hackish approach to get bl.ocks.org to display individual height |
|
d3.select(self.frameElement).style("height", height + "px"); |
|
|
|
</script> |
|
</body> |
|
</html> |