World Map with D3.js and TopoJSON data from World Atlas in
https://github.com/d3/d3
https://github.com/topojson/topojson
https://github.com/topojson/world-atlas
forked from piwodlaiwo's block: World Map with D3 and TopoJSON
license: mit |
World Map with D3.js and TopoJSON data from World Atlas in
https://github.com/d3/d3
https://github.com/topojson/topojson
https://github.com/topojson/world-atlas
forked from piwodlaiwo's block: World Map with D3 and TopoJSON
<!DOCTYPE html> | |
<meta charset="utf-8"> | |
<style> | |
body { | |
background: #fcfcfa; | |
} | |
.stroke { | |
fill: none; | |
stroke: #222; | |
stroke-width: 1px; | |
stroke-opacity:0.4 | |
} | |
.fill { | |
fill: #fff; | |
} | |
.graticule { | |
fill: none; | |
stroke: #222; | |
stroke-width: 0.5px; | |
stroke-opacity: 0.2; | |
} | |
.land { | |
fill: #9b9b9b; | |
} | |
.boundary { | |
fill: none; | |
stroke: black; | |
stroke-opacity: 0.4; | |
stroke-width: .5px; | |
} | |
#filter { | |
height: 0; | |
width: 0; | |
} | |
.annotation-group text { | |
font-family: Comic Sans MS; | |
fill: #222; | |
fill-opacity: 0.8; | |
stroke-opacity: 0; | |
} | |
.annotation-group path { | |
stroke: #222; | |
fill: #222; | |
} | |
.annotation-group .connector-dot { | |
stroke: #fff; | |
stroke-width: 0.5; | |
} | |
</style> | |
<body> | |
<script src="https://d3js.org/d3.v4.min.js"></script> | |
<script src="https://d3js.org/d3-geo-projection.v1.min.js"></script> | |
<script src="https://d3js.org/topojson.v2.min.js"></script> | |
<script src="https://cdnjs.cloudflare.com/ajax/libs/rough.js/3.1.0/rough.js"/></script> | |
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3-annotation/2.5.1/d3-annotation.min.js"></script> | |
<svg id='filter'> | |
<defs> | |
<filter id="outline"> | |
<feMorphology | |
in="SourceAlpha" | |
result="DILATED" | |
operator="dilate" | |
radius="2" /> | |
<feFlood | |
flood-color="#fff" | |
flood-opacity="1" | |
result="OUTLINECOLOUR" /> | |
<feComposite | |
in="OUTLINECOLOUR" | |
in2="DILATED" | |
operator="in" | |
result="OUTLINE" /> | |
<feMerge> | |
<feMergeNode in="OUTLINE" /> | |
<feMergeNode in="SourceGraphic" /> | |
</feMerge> | |
</filter> | |
</defs> | |
</svg> | |
<script> | |
const pathToRcPath = (d, color) => { | |
const rcPath = color ? rc.path(path(d), { fill: color }) : rc.path(path(d)); | |
const pathD = d3.select(rcPath) | |
.select('path') | |
.attr('d') | |
return pathD; | |
} | |
var width = 960, | |
height = 500; | |
var projection = d3.geoPolyhedralWaterman() | |
projection.rotate([-65,0]) | |
var path = d3.geoPath() | |
.projection(projection); | |
var graticule = d3.geoGraticule(); | |
var svg = d3.select("body").append("svg") | |
.attr("width", width) | |
.attr("height", height); | |
const rc = rough.svg(svg.node()); | |
var defs = svg.append("defs"); | |
defs.append("path") | |
.datum({type: "Sphere"}) | |
.attr("id", "sphere") | |
.attr("d", path); | |
defs.append("clipPath") | |
.attr("id", "clip") | |
.append("use") | |
.attr("xlink:href", "#sphere"); | |
svg.append("use") | |
.attr("class", "stroke") | |
.attr("xlink:href", "#sphere"); | |
svg | |
.append('path') | |
.datum({type: "Sphere"}) | |
.attr("id", "sphere") | |
.attr("d", pathToRcPath); | |
d3.json("https://gist.githubusercontent.com/mbostock/4090846/raw/d534aba169207548a8a3d670c9c2cc719ff05c47/world-110m.json", function(error, world) { | |
// draw gaticule | |
svg.append("path") | |
.datum(graticule) | |
.attr("class", "graticule") | |
.attr("clip-path", "url(#clip)") | |
.attr("d", pathToRcPath); | |
// draw land | |
const pathLandRough = path(topojson.feature(world, world.objects.land)); | |
const res = rc.path(pathLandRough, { fill: 'rgba(0,0,0,0.1)', fillStyle: 'cross-hatch', stroke: 'rgba(0,0,0,0.7)' }) | |
res.setAttribute('id', 'roughLand') | |
svg.node().appendChild(res) | |
svg.select('#roughLand').selectAll('path') | |
.each(function() { | |
const el = d3.select(this) | |
el.attr("clip-path", "url(#clip)") | |
}) | |
// draw country paths | |
svg.insert("path",) | |
.datum(topojson.mesh(world, world.objects.countries, function(a, b) { return a !== b; })) | |
.attr("class", "boundary") | |
.attr("clip-path", "url(#clip)") | |
.attr("d", pathToRcPath); | |
const createAnnotation = (title, label, lng, lat, offsetX, offsetY) => { | |
const xy = projection([lng, lat]) | |
return ( | |
{ | |
note: { | |
title: title, | |
label: label, | |
wrap: 150, | |
}, | |
connector: { | |
end: "dot", | |
type: "line", | |
}, | |
x: xy[0], | |
y: xy[1], | |
dx: offsetX, | |
dy: offsetY | |
}) | |
} | |
const annotations = [] | |
annotations.push(createAnnotation('Stockholm (1976)', 'First 20 years', 18.063240, 59.334591, 60, -150 )) | |
annotations.push(createAnnotation('London (2003)', 'MSc. Machine Learning', -0.118092, 51.509865, -225, 75 )) | |
annotations.push(createAnnotation('Shanghai (2008)', 'Made first professional dataviz', 121.469170, 31.224361, 250, 20 )) | |
annotations.push(createAnnotation('Copenhagen (2012)', 'Master of Disaster Management', 12.568337, 55.676098, 75, 225 )) | |
const makeAnnotations = d3.annotation() | |
.type(d3.annotationLabel) | |
.annotations(annotations) | |
d3.select("svg:last-of-type") | |
.append("g") | |
.attr("class", "annotation-group") | |
.call(makeAnnotations) | |
}); | |
d3.select(self.frameElement).style("height", height + "px"); | |
</script> |