Emulating Luis Sevillano's QGIS/Illustrator-produced globes with D3 and Canvas.
It uses a Canvas radial gradient to get the 'atmosphere' effect.
| license: mit | |
| border: none |
Emulating Luis Sevillano's QGIS/Illustrator-produced globes with D3 and Canvas.
It uses a Canvas radial gradient to get the 'atmosphere' effect.
| <!DOCTYPE html> | |
| <meta charset="utf-8" /> | |
| <body> | |
| <link href='https://fonts.googleapis.com/css?family=Roboto:400,400italic' rel='stylesheet' type='text/css'> | |
| <script src="https://d3js.org/d3.v3.min.js" charset="utf-8"></script> | |
| <script src="https://d3js.org/topojson.v1.min.js"></script> | |
| <script src="https://unpkg.com/rbush@1.4.3/rbush.js"></script> | |
| <script src="https://unpkg.com/spamjs@1.1.0/spam.min.js"></script> | |
| <script type='text/javascript'> | |
| var graticule = d3.geo.graticule(); | |
| // Canvas radial gradient | |
| var x = 960 / 2, | |
| y = 500 / 2, | |
| // Radii of the start glow. | |
| innerRadius = 60, | |
| // Radii of the end glow. | |
| outerRadius = 480, | |
| // Radius of the entire circle. | |
| radius = 480; | |
| // Canvas doesn't support letter spacing :___) | |
| // https://www.cs.tut.fi/~jkorpela/chars/spaces.html | |
| var circles = [ | |
| { name: "S W I T Z E R L A N D", coordinates: [8.424757, 46.94344] } | |
| ]; | |
| // SIX-PER-EM SPACE | |
| var continents = [ | |
| { name: "A M E R I C A", coordinates: [-80.262723, 40.26839] }, | |
| { name: "A F R I C A", coordinates: [5.889621, 5.409892] }, | |
| { name: "A S I A", coordinates: [80.041964, 35.059898] } | |
| ]; | |
| // THIN spaces | |
| var oceans = [ | |
| { name: "A T L A N T I C", coordinates: [-38.934598, 22.964389] }, | |
| { name: "O C E A N", coordinates: [-36.571317, 16.993163] } | |
| ]; | |
| d3.json("world.json", function(error, d) { | |
| topojson.presimplify(d); | |
| var map = new StaticCanvasMap({ | |
| element: "body", | |
| width: 960, | |
| height: 500, | |
| projection: d3.geo | |
| .orthographic() | |
| .clipAngle(90) | |
| .precision(0.1) | |
| .scale(240) | |
| .rotate([0, -45, 0]), | |
| data: [ | |
| { | |
| features: topojson.feature(d, d.objects["countries"]), | |
| static: { | |
| prepaint: function(parameters) { | |
| parameters.context.beginPath(); | |
| parameters.path({ type: "Sphere" }); | |
| parameters.context.lineWidth = 2; | |
| parameters.context.strokeStyle = "rgb(198, 197, 197)"; | |
| parameters.context.stroke(); | |
| var gradient = parameters.context.createRadialGradient( | |
| x, | |
| y, | |
| innerRadius, | |
| x, | |
| y, | |
| outerRadius | |
| ); | |
| gradient.addColorStop(0, "rgb(230, 239, 239)"); | |
| gradient.addColorStop(1, "rgb(200, 200, 200)"); | |
| parameters.context.fillStyle = gradient; | |
| parameters.context.fill(); | |
| parameters.context.beginPath(); | |
| parameters.path(graticule()); | |
| parameters.context.lineWidth = 0.2; | |
| parameters.context.strokeStyle = "rgba(30,30,30, 0.1)"; | |
| parameters.context.stroke(); | |
| }, | |
| paintfeature: function(parameters, d) { | |
| parameters.context.lineWidth = 0.5 / parameters.scale; | |
| parameters.context.strokeStyle = "rgba(0,0,0, 0.3)"; | |
| parameters.context.stroke(); | |
| if (d.properties.sovereignt === "Switzerland") { | |
| parameters.context.fillStyle = "orange"; | |
| } else { | |
| parameters.context.fillStyle = "#f1ede6"; | |
| } | |
| parameters.context.fill(); | |
| }, | |
| postpaint: function(parameters) { | |
| for (var i in circles) { | |
| parameters.context.beginPath(); | |
| parameters.context.textAlign = "center"; | |
| var circle = circles[i]; | |
| var projectedPoint = parameters.map | |
| .settings() | |
| .projection(circle.coordinates); | |
| parameters.context.arc( | |
| projectedPoint[0], | |
| projectedPoint[1] / parameters.scale, | |
| 10 / parameters.scale, | |
| 0, | |
| 2 * Math.PI, | |
| true | |
| ); | |
| parameters.context.font = "18px Roboto"; | |
| parameters.context.fillStyle = "rgba(70, 70, 70, 0.8)"; | |
| parameters.context.fillText( | |
| circle.name, | |
| projectedPoint[0], | |
| projectedPoint[1] - 15 | |
| ); | |
| parameters.context.strokeStyle = "rgb(126, 120, 117)"; | |
| parameters.context.lineWidth = 1.5 / parameters.scale; | |
| parameters.context.stroke(); | |
| } | |
| for (var i in continents) { | |
| parameters.context.beginPath(); | |
| parameters.context.textAlign = "center"; | |
| var continent = continents[i]; | |
| var projectedPoint = parameters.map | |
| .settings() | |
| .projection(continent.coordinates); | |
| parameters.context.font = "16px Roboto"; | |
| parameters.context.fillStyle = "rgba(155, 155, 155, 1)"; | |
| parameters.context.fillText( | |
| continent.name, | |
| projectedPoint[0], | |
| projectedPoint[1] - 15 | |
| ); | |
| parameters.context.stroke(); | |
| } | |
| for (var i in oceans) { | |
| parameters.context.beginPath(); | |
| parameters.context.textAlign = "center"; | |
| var ocean = oceans[i]; | |
| var projectedPoint = parameters.map | |
| .settings() | |
| .projection(ocean.coordinates); | |
| parameters.context.font = "italic 16px Roboto"; | |
| parameters.context.fillStyle = "rgba(122, 148, 149, 0.8)"; | |
| parameters.context.fillText( | |
| ocean.name, | |
| projectedPoint[0], | |
| projectedPoint[1] - 15 | |
| ); | |
| parameters.context.stroke(); | |
| } | |
| } | |
| } | |
| } | |
| ] | |
| }); | |
| map.init(); | |
| }); | |
| </script> |