Skip to content

Instantly share code, notes, and snippets.

@phoebebright
Forked from dwtkns/README.md
Created September 19, 2013 18:27
Show Gist options
  • Save phoebebright/6627763 to your computer and use it in GitHub Desktop.
Save phoebebright/6627763 to your computer and use it in GitHub Desktop.

Faux-3d SVG globe using d3.geo.orthographic and a few radial gradients. Labels offset or hidden based on radians from current map center to enhance the effect.

Uncomment svg.append("g").attr("class","countries") for hover-able country outlines.

<!DOCTYPE html>
<meta charset="utf-8">
<style>
.land {
fill: rgb(117, 87, 57);
stroke-opacity: 1;
}
.countries path {
stroke: rgb(80, 64, 39);
stroke-linejoin: round;
stroke-width:.5;
fill: rgb(117, 87, 57);
opacity: .1;
}
.countries path:hover {
fill-opacity:.1;
stroke-width:1;
opacity: 1;
}
.graticule {
fill: none;
stroke: black;
stroke-width:.5;
opacity:.2;
}
.labels {
font: 8px sans-serif;
fill: black;
opacity: .5;
}
.noclicks {
pointer-events:none;
}
.point{
opacity:.6;
}
</style>
<body>
<script src="http://d3js.org/d3.v3.min.js"></script>
<script src="http://d3js.org/queue.v1.min.js"></script>
<script src="http://d3js.org/topojson.v0.min.js"></script>
<script>
// Lots of code from:
// http://bl.ocks.org/3757125
// http://bl.ocks.org/3795040
d3.select(window)
.on("mousemove", mousemove)
.on("mouseup", mouseup);
var width = 960,
height = 500;
var proj = d3.geo.orthographic()
.scale(220)
.translate([width / 2, height / 2])
.clipAngle(90);
var path = d3.geo.path().projection(proj).pointRadius(1.5);
var graticule = d3.geo.graticule();
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height)
.on("mousedown", mousedown);
queue()
.defer(d3.json, "world-110m.json")
.defer(d3.json, "places.json")
.await(ready);
function ready(error, world, places) {
var ocean_fill = svg.append("defs").append("radialGradient")
.attr("id", "ocean_fill")
.attr("cx", "75%")
.attr("cy", "25%");
ocean_fill.append("stop").attr("offset", "5%").attr("stop-color", "#ddf");
ocean_fill.append("stop").attr("offset", "100%").attr("stop-color", "#9ab");
var globe_highlight = svg.append("defs").append("radialGradient")
.attr("id", "globe_highlight")
.attr("cx", "75%")
.attr("cy", "25%");
globe_highlight.append("stop")
.attr("offset", "5%").attr("stop-color", "#ffd")
.attr("stop-opacity","0.6");
globe_highlight.append("stop")
.attr("offset", "100%").attr("stop-color", "#ba9")
.attr("stop-opacity","0.2");
var globe_shading = svg.append("defs").append("radialGradient")
.attr("id", "globe_shading")
.attr("cx", "50%")
.attr("cy", "40%");
globe_shading.append("stop")
.attr("offset","50%").attr("stop-color", "#9ab")
.attr("stop-opacity","0")
globe_shading.append("stop")
.attr("offset","100%").attr("stop-color", "#3e6184")
.attr("stop-opacity","0.3")
var drop_shadow = svg.append("defs").append("radialGradient")
.attr("id", "drop_shadow")
.attr("cx", "50%")
.attr("cy", "50%");
drop_shadow.append("stop")
.attr("offset","20%").attr("stop-color", "#000")
.attr("stop-opacity",".5")
drop_shadow.append("stop")
.attr("offset","100%").attr("stop-color", "#000")
.attr("stop-opacity","0")
svg.append("ellipse")
.attr("cx", 440).attr("cy", 450)
.attr("rx", proj.scale()*.90)
.attr("ry", proj.scale()*.25)
.attr("class", "noclicks")
.style("fill", "url(#drop_shadow)");
svg.append("circle")
.attr("cx", width / 2).attr("cy", height / 2)
.attr("r", proj.scale())
.attr("class", "noclicks")
.style("fill", "url(#ocean_fill)");
svg.append("path")
.datum(topojson.object(world, world.objects.land))
.attr("class", "land")
.attr("d", path);
svg.append("path")
.datum(graticule)
.attr("class", "graticule noclicks")
.attr("d", path);
svg.append("circle")
.attr("cx", width / 2).attr("cy", height / 2)
.attr("r", proj.scale())
.attr("class","noclicks")
.style("fill", "url(#globe_highlight)");
svg.append("circle")
.attr("cx", width / 2).attr("cy", height / 2)
.attr("r", proj.scale())
.attr("class","noclicks")
.style("fill", "url(#globe_shading)");
svg.append("g").attr("class","points")
.selectAll("text").data(places.features)
.enter().append("path")
.attr("class", "point")
.attr("d", path);
svg.append("g").attr("class","labels")
.selectAll("text").data(places.features)
.enter().append("text")
.attr("class", "label")
.text(function(d) { return d.properties.name })
// uncomment for hover-able country outlines
// svg.append("g").attr("class","countries")
// .selectAll("path")
// .data(topojson.object(world, world.objects.countries).geometries)
// .enter().append("path")
// .attr("d", path);
position_labels();
}
function position_labels() {
var centerPos = proj.invert([width/2,height/2]);
var arc = d3.geo.greatArc();
svg.selectAll(".label")
.attr("text-anchor",function(d) {
var x = proj(d.geometry.coordinates)[0];
return x < width/2-20 ? "end" :
x < width/2+20 ? "middle" :
"start"
})
.attr("transform", function(d) {
var loc = proj(d.geometry.coordinates),
x = loc[0],
y = loc[1];
var offset = x < width/2 ? -5 : 5;
return "translate(" + (x+offset) + "," + (y-2) + ")"
})
.style("display",function(d) {
var d = arc.distance({source: d.geometry.coordinates, target: centerPos});
return (d > 1.57) ? 'none' : 'inline';
})
}
// modified from http://bl.ocks.org/1392560
var m0, o0;
function mousedown() {
m0 = [d3.event.pageX, d3.event.pageY];
o0 = proj.rotate();
d3.event.preventDefault();
}
function mousemove() {
if (m0) {
var m1 = [d3.event.pageX, d3.event.pageY]
, o1 = [o0[0] + (m1[0] - m0[0]) / 6, o0[1] + (m0[1] - m1[1]) / 6];
o1[1] = o1[1] > 30 ? 30 :
o1[1] < -30 ? -30 :
o1[1];
proj.rotate(o1);
refresh();
}
}
function mouseup() {
if (m0) {
mousemove();
m0 = null;
}
}
function refresh() {
svg.selectAll(".land").attr("d", path);
svg.selectAll(".countries path").attr("d", path);
svg.selectAll(".graticule").attr("d", path);
svg.selectAll(".point").attr("d", path);
position_labels();
}
</script>
Display the source blob
Display the rendered blob
Raw
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Display the source blob
Display the rendered blob
Raw
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment