Skip to content

Instantly share code, notes, and snippets.

@jthomassie
Last active June 13, 2023 20:25
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save jthomassie/9569067 to your computer and use it in GitHub Desktop.
Save jthomassie/9569067 to your computer and use it in GitHub Desktop.
d3 globe arcs
Display the source blob
Display the rendered blob
Raw
{
"type": "FeatureCollection",
"features": [
{ "type": "Feature", "properties": { "scalerank": 0, "labelrank": 1, "featurecla": "Populated place", "name": "Los Angeles", "nameascii": "Los Angeles", "adm0name": "United States of America", "adm0_a3": "USA", "adm1name": "California", "iso_a2": "US", "note": null, "latitude": 33.989978250199997, "longitude": -118.179980511, "changed": 0.0, "namediff": 0, "diffnote": null, "pop_max": 12500000, "pop_min": 3694820, "pop_other": 142265, "rank_max": 14, "rank_min": 12, "geonameid": 5368361.0, "meganame": "Los Angeles-Long Beach-Santa Ana", "ls_name": "Los Angeles1", "ls_match": 1, "checkme": 0 }, "geometry": { "type": "Point", "coordinates": [ -118.181926369940413, 33.991924108765431 ] } },
{ "type": "Feature", "properties": { "scalerank": 0, "labelrank": 1, "featurecla": "Admin-0 capital", "name": "Washington, D.C.", "nameascii": "Washington, D.C.", "adm0name": "United States of America", "adm0_a3": "USA", "adm1name": "District of Columbia", "iso_a2": "US", "note": null, "latitude": 38.899549376499998, "longitude": -77.009418580800002, "changed": 0.0, "namediff": 0, "diffnote": null, "pop_max": 4338000, "pop_min": 552433, "pop_other": 2175991, "rank_max": 12, "rank_min": 11, "geonameid": 4140963.0, "meganame": "Washington, D.C.", "ls_name": "Washington, D.C.", "ls_match": 1, "checkme": 5 }, "geometry": { "type": "Point", "coordinates": [ -77.011364439437159, 38.901495235087054 ] } },
{ "type": "Feature", "properties": { "scalerank": 0, "labelrank": 1, "featurecla": "Populated place", "name": "New York", "nameascii": "New York", "adm0name": "United States of America", "adm0_a3": "USA", "adm1name": "New York", "iso_a2": "US", "note": null, "latitude": 40.749979064, "longitude": -73.980016928799998, "changed": 0.0, "namediff": 0, "diffnote": null, "pop_max": 19040000, "pop_min": 8008278, "pop_other": 9292603, "rank_max": 14, "rank_min": 13, "geonameid": 5128581.0, "meganame": "New York-Newark", "ls_name": "New York", "ls_match": 1, "checkme": 0 }, "geometry": { "type": "Point", "coordinates": [ -73.981962787406815, 40.75192492259464 ] } },
{ "type": "Feature", "properties": { "scalerank": 0, "labelrank": 2, "featurecla": "Admin-0 capital", "name": "Moscow", "nameascii": "Moscow", "adm0name": "Russia", "adm0_a3": "RUS", "adm1name": "Moskva", "iso_a2": "RU", "note": null, "latitude": 55.7521641226, "longitude": 37.615522825900001, "changed": 0.0, "namediff": 0, "diffnote": null, "pop_max": 10452000, "pop_min": 10452000, "pop_other": 10585385, "rank_max": 14, "rank_min": 14, "geonameid": 524901.0, "meganame": "Moskva", "ls_name": "Moscow", "ls_match": 1, "checkme": 0 }, "geometry": { "type": "Point", "coordinates": [ 37.613576967271399, 55.754109981248178 ] } },
{ "type": "Feature", "properties": { "scalerank": 0, "labelrank": 2, "featurecla": "Admin-0 capital", "name": "Mexico City", "nameascii": "Mexico City", "adm0name": "Mexico", "adm0_a3": "MEX", "adm1name": "Distrito Federal", "iso_a2": "MX", "note": null, "latitude": 19.442442442800001, "longitude": -99.130988201700006, "changed": 0.0, "namediff": 0, "diffnote": null, "pop_max": 19028000, "pop_min": 10811002, "pop_other": 10018444, "rank_max": 14, "rank_min": 14, "geonameid": 3530597.0, "meganame": "Ciudad de México", "ls_name": "Mexico City", "ls_match": 1, "checkme": 0 }, "geometry": { "type": "Point", "coordinates": [ -99.132934060293906, 19.444388301415472 ] } },
{ "type": "Feature", "properties": { "scalerank": 0, "labelrank": 2, "featurecla": "Admin-0 capital alt", "name": "Lagos", "nameascii": "Lagos", "adm0name": "Nigeria", "adm0_a3": "NGA", "adm1name": "Lagos", "iso_a2": "NG", "note": null, "latitude": 6.44326165348, "longitude": 3.39153107121, "changed": 4.0, "namediff": 0, "diffnote": "Location adjusted. Changed scale rank.", "pop_max": 9466000, "pop_min": 1536, "pop_other": 6567892, "rank_max": 13, "rank_min": 3, "geonameid": 735497.0, "meganame": "Lagos", "ls_name": "Lagos", "ls_match": 1, "checkme": 0 }, "geometry": { "type": "Point", "coordinates": [ 3.389585212598433, 6.445207512093191 ] } },
{ "type": "Feature", "properties": { "scalerank": 0, "labelrank": 1, "featurecla": "Admin-0 capital", "name": "Beijing", "nameascii": "Beijing", "adm0name": "China", "adm0_a3": "CHN", "adm1name": "Beijing", "iso_a2": "CN", "note": null, "latitude": 39.928892231299997, "longitude": 116.388285684, "changed": 0.0, "namediff": 0, "diffnote": null, "pop_max": 11106000, "pop_min": 7480601, "pop_other": 9033231, "rank_max": 14, "rank_min": 13, "geonameid": 1816670.0, "meganame": "Beijing", "ls_name": "Beijing", "ls_match": 1, "checkme": 0 }, "geometry": { "type": "Point", "coordinates": [ 116.386339825659434, 39.930838089909059 ] } },
{ "type": "Feature", "properties": { "scalerank": 0, "labelrank": 1, "featurecla": "Admin-0 capital", "name": "Jakarta", "nameascii": "Jakarta", "adm0name": "Indonesia", "adm0_a3": "IDN", "adm1name": "Jakarta Raya", "iso_a2": "ID", "note": null, "latitude": -6.17441770541, "longitude": 106.829437621, "changed": 0.0, "namediff": 0, "diffnote": null, "pop_max": 9125000, "pop_min": 8540121, "pop_other": 9129613, "rank_max": 13, "rank_min": 13, "geonameid": 1642911.0, "meganame": "Jakarta", "ls_name": "Jakarta", "ls_match": 1, "checkme": 0 }, "geometry": { "type": "Point", "coordinates": [ 106.827491762470117, -6.172471846798885 ] } },
{ "type": "Feature", "properties": { "scalerank": 0, "labelrank": 1, "featurecla": "Admin-1 capital", "name": "Shanghai", "nameascii": "Shanghai", "adm0name": "China", "adm0_a3": "CHN", "adm1name": "Shanghai", "iso_a2": "CN", "note": null, "latitude": 31.216452452599999, "longitude": 121.436504678, "changed": 0.0, "namediff": 0, "diffnote": null, "pop_max": 14987000, "pop_min": 14608512, "pop_other": 16803572, "rank_max": 14, "rank_min": 14, "geonameid": 1796236.0, "meganame": "Shanghai", "ls_name": "Shanghai", "ls_match": 1, "checkme": 0 }, "geometry": { "type": "Point", "coordinates": [ 121.434558819820154, 31.218398311228327 ] } },
{ "type": "Feature", "properties": { "scalerank": 0, "labelrank": 2, "featurecla": "Admin-0 capital", "name": "Tokyo", "nameascii": "Tokyo", "adm0name": "Japan", "adm0_a3": "JPN", "adm1name": "Tokyo", "iso_a2": "JP", "note": null, "latitude": 35.685016905799998, "longitude": 139.751407429000011, "changed": 0.0, "namediff": 0, "diffnote": null, "pop_max": 35676000, "pop_min": 8336599, "pop_other": 12945252, "rank_max": 14, "rank_min": 13, "geonameid": 1850147.0, "meganame": "Tokyo", "ls_name": "Tokyo", "ls_match": 1, "checkme": 0 }, "geometry": { "type": "Point", "coordinates": [ 139.749461570544668, 35.686962764371174 ] } },
{ "type": "Feature", "properties": { "scalerank": 0, "labelrank": 1, "featurecla": "Admin-1 capital", "name": "Mumbai", "nameascii": "Mumbai", "adm0name": "India", "adm0_a3": "IND", "adm1name": "Maharashtra", "iso_a2": "IN", "note": null, "latitude": 19.016990375700001, "longitude": 72.856989297400006, "changed": 0.0, "namediff": 0, "diffnote": null, "pop_max": 18978000, "pop_min": 12691836, "pop_other": 12426085, "rank_max": 14, "rank_min": 14, "geonameid": 1275339.0, "meganame": "Mumbai", "ls_name": "Mumbai", "ls_match": 1, "checkme": 0 }, "geometry": { "type": "Point", "coordinates": [ 72.855043438766472, 19.018936234356602 ] } },
{ "type": "Feature", "properties": { "scalerank": 0, "labelrank": 1, "featurecla": "Admin-1 capital", "name": "Kolkata", "nameascii": "Kolkata", "adm0name": "India", "adm0_a3": "IND", "adm1name": "West Bengal", "iso_a2": "IN", "note": null, "latitude": 22.494969298299999, "longitude": 88.324675658100006, "changed": 4.0, "namediff": 1, "diffnote": "Name changed. Changed scale rank.", "pop_max": 14787000, "pop_min": 4631392, "pop_other": 7783716, "rank_max": 14, "rank_min": 12, "geonameid": 1275004.0, "meganame": "Kolkata", "ls_name": "Calcutta", "ls_match": 1, "checkme": 0 }, "geometry": { "type": "Point", "coordinates": [ 88.32272979950551, 22.496915156896421 ] } },
{ "type": "Feature", "properties": { "scalerank": 0, "labelrank": 1, "featurecla": "Populated place", "name": "Rio de Janeiro", "nameascii": "Rio de Janeiro", "adm0name": "Brazil", "adm0_a3": "BRA", "adm1name": "Rio de Janeiro", "iso_a2": "BR", "note": null, "latitude": -22.9250231742, "longitude": -43.225020794199999, "changed": 0.0, "namediff": 0, "diffnote": null, "pop_max": 11748000, "pop_min": 2010175, "pop_other": 1821489, "rank_max": 14, "rank_min": 12, "geonameid": 3451190.0, "meganame": "Rio de Janeiro", "ls_name": "Rio de Janeiro", "ls_match": 1, "checkme": 0 }, "geometry": { "type": "Point", "coordinates": [ -43.226966652843657, -22.923077315615956 ] } },
{ "type": "Feature", "properties": { "scalerank": 0, "labelrank": 1, "featurecla": "Admin-1 capital", "name": "Sao Paulo", "nameascii": "Sao Paulo", "adm0name": "Brazil", "adm0_a3": "BRA", "adm1name": "São Paulo", "iso_a2": "BR", "note": null, "latitude": -23.558679587, "longitude": -46.625019980399998, "changed": 0.0, "namediff": 0, "diffnote": null, "pop_max": 18845000, "pop_min": 10021295, "pop_other": 11522944, "rank_max": 14, "rank_min": 14, "geonameid": 3448439.0, "meganame": "São Paulo", "ls_name": "Sao Paolo", "ls_match": 1, "checkme": 0 }, "geometry": { "type": "Point", "coordinates": [ -46.626965839055231, -23.556733728378958 ] } },
{ "type": "Feature", "properties": { "scalerank": 0, "labelrank": 0, "featurecla": "Admin-0 capital", "name": "Singapore", "nameascii": "Singapore", "adm0name": "Singapore", "adm0_a3": "SGP", "adm1name": null, "iso_a2": "SG", "note": null, "latitude": 1.29303346649, "longitude": 103.855820678, "changed": 0.0, "namediff": 0, "diffnote": null, "pop_max": 5183700, "pop_min": 3289529, "pop_other": 3314179, "rank_max": 13, "rank_min": 12, "geonameid": 1880252.0, "meganame": "Singapore", "ls_name": "Singapore", "ls_match": 1, "checkme": 5 }, "geometry": { "type": "Point", "coordinates": [ 103.853874819099019, 1.294979325105942 ] } },
{ "type": "Feature", "properties": { "scalerank": 0, "labelrank": 0, "featurecla": "Admin-0 region capital", "name": "Hong Kong", "nameascii": "Hong Kong", "adm0name": "Hong Kong S.A.R.", "adm0_a3": "HKG", "adm1name": null, "iso_a2": "HK", "note": null, "latitude": 22.304980895, "longitude": 114.185009317, "changed": 0.0, "namediff": 0, "diffnote": null, "pop_max": 7206000, "pop_min": 4551579, "pop_other": 4549026, "rank_max": 13, "rank_min": 12, "geonameid": 1819729.0, "meganame": "Hong Kong", "ls_name": "Hong Kong", "ls_match": 1, "checkme": 0 }, "geometry": { "type": "Point", "coordinates": [ 114.183063458463039, 22.30692675357551 ] } }
]
}
<!DOCTYPE html>
<meta charset="utf-8">
<style>
.graticule {
fill: none;
stroke: #ccc;
stroke-width: 0.7;
stroke-opacity: 1.0;
}
.edge {
fill: none;
stroke: #444;
stroke-width: 0.8;
stroke-opacity: 1.0;
}
.land {
fill: #ccc;
fill-opacity: 0.6;
stroke: #999;
stroke-opacity: 1.0;
stroke-width: 1.0;
}
.countries path {
fill: #ddd;
fill-opacity: 0.4;
stroke: #444;
stroke-linejoin: round;
stroke-width: 0.4;
stroke-opacity: 0.2;
}
.countries path:hover {
fill-opacity: 0.6;
stroke-width: 1.1;
stroke-opacity: 0.5;
}
.labels {
font: 12px sans-serif;
fill: #444;
}
.point{
fill: #444;
stroke: #444;
stroke-width: 1;
stroke-opacity: 1;
}
.noclick {
pointer-events:none;
}
.arcs {
opacity:.1;
stroke: gray;
stroke-width: 3;
}
.flyers {
stroke-width:1;
opacity: .6;
stroke: darkred;
}
.arc, .flyer {
stroke-linejoin: round;
fill:none;
}
.arc { }
.flyer { }
.flyer:hover { }
</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>
d3.select(window)
.on("mousemove", mousemove)
.on("mouseup", mouseup);
var width = 960,
height = 500;
var center = [50.0, -40.0];
var proj = d3.geo.orthographic()
.translate([width / 2, height / 2])
.scale(220)
.precision(0.3)
.rotate(center)
.clipAngle(90);
var sky = d3.geo.orthographic()
.translate([width / 2, height / 2])
.clipAngle(90)
.scale(280);
var path = d3.geo.path().projection(proj).pointRadius(2);
var graticule = d3.geo.graticule();
var swoosh = d3.svg.line()
.x(function(d) { return d[0]; })
.y(function(d) { return d[1]; })
.interpolate("basis");
var links = [],
arcLines = [];
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, "arc-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%");
// water sphere
svg.append("path")
.datum({type: "Sphere"})
.attr("class", "water noclick")
.attr("d", path)
.style("fill", "#fff");
// graticule
svg.append("path")
.datum(graticule)
.attr("class", "graticule noclick")
.attr("d", path);
// land shape
svg.append("path")
.datum(topojson.object(world, world.objects.land))
.attr("class", "land noclicks")
.attr("d", path);
// edge sphere
svg.append("path")
.datum({type: "Sphere"})
.attr("class", "edge noclick")
.attr("d", path);
svg.append("g")
.attr("class","points")
.selectAll("text").data(places.features)
.enter().append("path")
.attr("class", "point")
.attr("d", path);
// spawn links between cities as source/target coord pairs
places.features.forEach(function(a) {
places.features.forEach(function(b) {
if (a !== b) {
links.push({
source: a.geometry.coordinates,
target: b.geometry.coordinates
});
}
});
});
// build geoJSON features from links array
links.forEach(function(e,i,a) {
var feature = { "type": "Feature", "geometry": { "type": "LineString", "coordinates": [e.source,e.target] }};
arcLines.push(feature);
});
svg.append("g").attr("class","arcs")
.selectAll("path").data(arcLines)
.enter().append("path")
.attr("class","arc")
.attr("d",path);
svg.append("g").attr("class","flyers")
.selectAll("path").data(links)
.enter().append("path")
.attr("class","flyer")
.attr("d", function(d) {
// return swoosh(flying_arc(d));
var ddd = "M";
var pts = flying_arc(d);
for (var i = 0; i < pts.length; i++ ) {
if(i == 1) {
ddd += "C";
}
ddd += pts[i][0];
ddd += ",";
ddd += pts[i][1];
if(i < pts.length-1) {
ddd += " ";
}
}
//console.log(ddd);
return ddd;
});
proj.rotate(center);
sky.rotate(center);
refresh();
}
function flying_arc(pts) {
var source = pts.source,
target = pts.target;
var mid = location_along_arc(source, target, 0.35);
var mid2 = location_along_arc(source, target, 0.65);
var result = [
proj(source),
sky(mid),
sky(mid2),
proj(target)
];
//console.log(proj(source));
return result;
}
function refresh() {
svg.selectAll(".land").attr("d", path);
svg.selectAll(".point").attr("d", path);
svg.selectAll("path").attr("d", path);
svg.selectAll(".arc")
.attr("d", path)
.attr("opacity", function(d) {
return fade_at_edge(d);
});
svg.selectAll(".flyer")
.attr("d", function(d) {
return swoosh(flying_arc(d));
})
.attr("opacity", function(d) {
return fade_at_edge(d);
});
}
function fade_at_edge(d) {
var centerPos = proj.invert([width/2,height/2]),
arc = d3.geo.greatArc(),
start, end;
// function is called on 2 different data structures..
if (d.source) {
start = d.source,
end = d.target;
} else {
start = d.geometry.coordinates[0];
end = d.geometry.coordinates[1];
}
var start_dist = 1.57 - arc.distance({source: start, target: centerPos}),
end_dist = 1.57 - arc.distance({source: end, target: centerPos});
var fade = d3.scale.linear().domain([-0.1, 0]).range([0, 0.1]) ;
var dist = start_dist < end_dist ? start_dist : end_dist;
return fade(dist);
}
function location_along_arc(start, end, loc) {
var interpolator = d3.geo.interpolate(start,end);
return interpolator(loc);
}
// 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] > 55 ? 55 :
o1[1] < -55 ? -55 :
o1[1];
console.log(o1);
proj.rotate(o1);
sky.rotate(o1);
refresh();
}
}
function mouseup() {
if (m0) {
mousemove();
m0 = null;
}
}
</script>
Display the source blob
Display the rendered blob
Raw
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
@robin-rpr
Copy link

Great Job! I created a Demo on Codepen from it https://codepen.io/_robin/pen/OJEdyVL

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment