Skip to content

Instantly share code, notes, and snippets.

@KoGor
Last active December 25, 2015 18:59
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 KoGor/7024546 to your computer and use it in GitHub Desktop.
Save KoGor/7024546 to your computer and use it in GitHub Desktop.
Globe to Map II

Globe rotating manually via drag event, 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) and map always cut along antimeridian.

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="metaGlobe2Map2.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,
sens = 0.25;
var projectionGlobe = d3.geo.orthographic()
.scale(240)
.rotate([0, 0])
.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");
//Rotate to default before animation
function defaultRotate() {
d3.transition()
.duration(1500)
.tween("rotate", function() {
var r = d3.interpolate(projection.rotate(), [0, 0]);
return function(t) {
projection.rotate(r(t));
g.selectAll("path").attr("d", path);
};
})
};
//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);
//Drag event
world.call(d3.behavior.drag()
.origin(function() { var r = projection.rotate(); return {x: r[0] / sens, y: -r[1] / sens}; })
.on("drag", function() {
var λ = d3.event.x * sens,
φ = -d3.event.y * sens,
rotate = projection.rotate();
//Restriction for rotating upside-down
φ = φ > 30 ? 30 :
φ < -30 ? -30 :
φ;
projection.rotate([λ, φ]);
g.selectAll("path.ortho").attr("d", path);
g.selectAll(".focused").classed("focused", focused = false);
}))
//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) {
defaultRotate();
setTimeout(function() {
g.selectAll(".ortho").classed("ortho", ortho = false);
projection = projectionMap;
path.projection(projection);
g.selectAll("path").transition().duration(5000).attr("d", path);
}
, 1600);
}
});
//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);
}
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(5000).attr("d", path)
g.selectAll("path").classed("ortho", ortho = true);
}
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment