Skip to content

Instantly share code, notes, and snippets.

@KoGor
Last active June 9, 2017 05:10
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save KoGor/7023703 to your computer and use it in GitHub Desktop.
Save KoGor/7023703 to your computer and use it in GitHub Desktop.
Globe to Map

Globe rotating all the time, when mouse click event fires - transition begins. For transition backwards use double click on any country. In this example transition is simple path to path (shortest way).

For other variations of transition from Orthographic to Equirectangular and back check these examples:

This projected is licensed under the terms of the MIT license.

<!DOCTYPE html>
<html lang="en">
<meta charset="utf-8">
<head>
<title>Globe to Map transition</title>
</head>
<body>
<!-- start -->
<div id="map">
<link href="mapStyle.css" rel="stylesheet">
<script src="http://d3js.org/d3.v3.min.js"></script>
<script src="http://d3js.org/topojson.v1.min.js"></script>
<script src="http://d3js.org/queue.v1.min.js"></script>
<script src="metaGlobe2Map.js"></script>
</div>
<!-- end -->
</body>
</html>
#map {
position: relative;
}
svg {
background: #FFF;
pointer-events: all;
}
.zone {
stroke-width: .5px;
stroke-linejoin: round;
cursor: pointer;
}
.zoneTooltip {
position: absolute;
display: none;
pointer-events: none;
background: #fff;
padding: 3px;
text-align: left;
border: solid #ccc 1px;
color: #666;
font-size: 12px;
font-family: sans-serif;
-webkit-border-radius: 5px;
-moz-border-radius: 5px;
border-radius: 5px;
}
.infoLabel {
position: absolute;
bottom: 15px;
left: 10px;
display: none;
border: solid #ccc 1px;
color: #666;
background: #FFFFFF;
-webkit-border-radius: 3px;
-moz-border-radius: 3px;
border-radius: 3px;
-webkit-box-shadow: 0px 0px 3px #66C2FF;
-moz-box-shadow: 0px 0px 3px #66C2FF;
box-shadow: 0px 0px 3px #66C2FF;
text-align: left;
padding: 2px;
font-size: 12px;
font-family: sans-serif;
pointer-events: none;
}
.infoLabel span {
margin: 0px 5px;
}
.mapData {
fill: #E6E6E6;
stroke: #ffffff;
stroke-width: 1px;
}
.ortho {
fill: #E6E6E6;
stroke: #E6E6E6;
stroke-width: 1px;
}
.mapData:hover {
fill: #66C2FF;
}
.focused {
fill: #FF7519;
}
var mapWidth = 960,
mapHeight = 500,
focused = false,
ortho = true,
speed = -7e-3,
start = Date.now(),
corr = 0;
var projectionGlobe = d3.geo.orthographic()
.scale(240)
.translate([mapWidth / 2, mapHeight / 2])
.clipAngle(90);
var projectionMap = d3.geo.equirectangular()
.scale(145)
.translate([mapWidth / 2, mapHeight / 2])
var projection = projectionGlobe;
var path = d3.geo.path()
.projection(projection);
var svgMap = d3.select("div#map").append("svg")
.attr("overflow", "hidden")
.attr("width", mapWidth)
.attr("height", mapHeight);
var zoneTooltip = d3.select("div#map").append("div").attr("class", "zoneTooltip"),
infoLabel = d3.select("div#map").append("div").attr("class", "infoLabel");
var g = svgMap.append("g");
//Starter for function AFTER All transitions
function endall(transition, callback) {
var n = 0;
transition
.each(function() { ++n; })
.each("end", function() { if (!--n) callback.apply(this, arguments); });
}
//Loading data
queue()
.defer(d3.json, "/d/5685937/world-110m.json")
.defer(d3.tsv, "/d/5685937/world-110m-country-names.tsv")
.await(ready);
function ready(error, world, countryData) {
var countryById = {},
countries = topojson.feature(world, world.objects.countries).features;
//Adding countries by name
countryData.forEach(function(d) {
countryById[d.id] = d.name;
});
//Drawing countries on the globe
var world = g.selectAll("path").data(countries);
world.enter().append("path")
.attr("class", "mapData")
.attr("d", path)
.classed("ortho", ortho = true);
//Events processing
world.on("mouseover", function(d) {
if (ortho === true) {
infoLabel.text(countryById[d.id])
.style("display", "inline");
} else {
zoneTooltip.text(countryById[d.id])
.style("left", (d3.event.pageX + 7) + "px")
.style("top", (d3.event.pageY - 15) + "px")
.style("display", "block");
}
})
.on("mouseout", function(d) {
if (ortho === true) {
infoLabel.style("display", "none");
} else {
zoneTooltip.style("display", "none");
}
})
.on("mousemove", function() {
if (ortho === false) {
zoneTooltip.style("left", (d3.event.pageX + 7) + "px")
.style("top", (d3.event.pageY - 15) + "px");
}
})
.on("click", function(d) {
if (focused === d) return reset();
g.selectAll(".focused").classed("focused", false);
d3.select(this).classed("focused", focused = d);
infoLabel.text(countryById[d.id])
.style("display", "inline");
//Transforming Globe to Map
if (ortho === true) {
corr = projection.rotate()[0]; // <- save last rotation angle
g.selectAll(".ortho").classed("ortho", ortho = false);
projection = projectionMap;
path.projection(projection);
g.selectAll("path").transition().duration(3000).attr("d", path);
}
});
//Globe rotating via timer
d3.timer(function() {
var λ = speed * (Date.now() - start);
projection.rotate([λ + corr, -5]);
g.selectAll(".ortho").attr("d", path);
});
//Adding extra data when focused
function focus(d) {
if (focused === d) return reset();
g.selectAll(".focused").classed("focused", false);
d3.select(this).classed("focused", focused = d);
}
//Reset projection
function reset() {
g.selectAll(".focused").classed("focused", focused = false);
infoLabel.style("display", "none");
zoneTooltip.style("display", "none");
//Transforming Map to Globe
projection = projectionGlobe;
path.projection(projection);
g.selectAll("path").transition()
.duration(3000).attr("d", path)
.call(endall, function() {
g.selectAll("path").classed("ortho", ortho = true);
start = Date.now(); // <- reset start for rotation
});
}
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment