<!DOCTYPE html> <head> <meta charset="utf-8"> <script src="https://d3js.org/d3.v4.min.js"></script> <script src="https://d3js.org/topojson.v2.min.js"></script> <script src="https://unpkg.com/versor"></script> <script src="https://unpkg.com/d3-inertia"></script> <style> body { margin:0; position:fixed; top:0; right:0; bottom:0; left:0; background: #fcfcfa; font-family: "Roboto", sans-serif; } main { width: 25vw; margin: 0 auto; padding: 5vw; max-width: 50em; } circle:hover { stroke: #002147; } .container { display: flex; /* or inline-flex */ } .world { cursor: move; /* fallback if grab cursor is unsupported */ cursor: grab; cursor: -moz-grab; cursor: -webkit-grab; } </style> </head> <body> <div class="container"> <svg> <defs> <!-- <pattern id="usflag" x="0" y="0" width="50" height="25" patternUnits="objectBoundingBox"> <image xlink:href="usflag.png" x="-15" y="-5" width="50" height="25"></image> </pattern> --> <!-- <pattern id="usflag" x="0" y="0" width="25" height="25" patternUnits="userSpaceOnUse"> <rect width="10" height="3" fill="#BF0A30"/> <rect width="10" height="4" fill="#ffffff"/> <rect width="10" height="3" fill="#BF0A30"/> </pattern> --> <linearGradient id="gradBlue" x1="0%" y1="0%" x2="100%" y2="0%"> <stop offset="0%" style="stop-color:#005C99;stop-opacity:1" /> <stop offset="100%" style="stop-color:#0099FF;stop-opacity:1" /> </linearGradient> <radialGradient id="gradient" cx="75%" cy="25%"> <stop offset="5%" stop-color="#ffd"></stop><stop offset="100%" stop-color="#ba9"></stop> </radialGradient> </defs> </svg> <main> <h1></h1> <h2>Click on a US Flag!</h2> <p id="notes"> </p> <p>Data from David Vine"s research for <a href="http://www.davidvine.net/base-nation.html" target="_blank">Base Nation book</a></p> </main> </div> <script> var title = d3.select("h1"); var notesP = d3.select("#notes"); var notes = d3.select("h2"); var width = 960, height = 700, margin = { top: 10, right: 10, bottom: 10, left: 10 }, originalScale = height / 2.0, scale = originalScale, translation = [width / 2, height / 2], scaleChange, rotation; var svg = d3.select("svg") .attr("width", width + margin.left + margin.right) .attr("height", height + margin.top + margin.bottom) .append("g") .attr("transform", "translate(" + margin.top + ", " + margin.left + ")"); var sphere = {type: "Sphere"}; var graticule = d3.geoGraticule(); var projection = d3.geoOrthographic() .scale(scale) .translate(translation) .clipAngle(90); var path = d3.geoPath() .projection(projection); d3.queue() .defer(d3.csv, "bases.csv") .defer(d3.csv, "lilypads.csv") .defer(d3.csv, "usfunded.csv") .defer(d3.json, "https://unpkg.com/world-atlas@1/world/110m.json") .await(load); function load(error, bases, lilypads, usfunded, world,) { if (error) throw error; var countries = topojson.feature(world, world.objects.countries).features; grid = graticule(); svg.append("g") .attr("id", "globegroup") .append("path") .datum(sphere) .attr("class", "world") .attr("id", "sphere") .attr("fill", "url(#gradBlue)") .attr("stroke", "#ccc") .attr("stroke-width", 1) .attr("z-index", 100); svg.selectAll(".grid") .data([grid]) .enter() .append("path") .classed("world", true) .classed("grid", true) .attr("fill", "none") .attr("stroke", "#ffd") .attr("stroke-width", 1); svg.selectAll(".country") .data(countries) .enter().insert("path") .attr("class", "countries") .attr("class", "world") .attr("id", function (d) { return d.id; }) .attr("fill", "#ccc") .attr("stroke", "#fff") .append("title") .text(function(d) { console.log(d); }); //return countrycodesDict[d.id]; svg.selectAll(".lilypad") .data(lilypads) .enter().append("circle") .attr("fill", "red") .attr("r", 4) .append("title") .text(function(d) { return "Lilypad: " + d.name }); svg.selectAll(".usfunded") .data(usfunded) .enter().append("circle") .attr("fill", "red") .attr("r", 2.5) .append("title") .text(function(d) { return "US Funded: " + d.name }); svg.selectAll(".base") .data(bases) .enter().append("circle") .attr("stroke", "red") .attr("fill", "#fff") .attr("r", 5.5) .append("title") .text(function(d) { return "Base: " + d.name }); svg.select("#USA") .attr("stroke", "#000") .attr("stroke-width", 2); svg.selectAll("circle") .on("click", function(d) { title.html(d.name + ", " + d.country); notes.html("Notes"); notesP.html(d.notes); }) reproject(); // Zoom and pan set-up var zoom = d3.zoom() .scaleExtent([0.5, 4]) .on("zoom", zoomed) d3.select("svg").call(zoom); var previousScaleFactor = 1; function zoomed() { var dx = d3.event.sourceEvent.movementX; var dy = d3.event.sourceEvent.movementY; var event = d3.event.sourceEvent.type; if (event === "wheel") { scaleFactor = d3.event.transform.k; scaleChange = scaleFactor - previousScaleFactor; scale = scale + scaleChange * originalScale; projection.scale(scale); previousScaleFactor = scaleFactor; } reproject(); } // zoomed() } // load() d3.geoInertiaDrag(svg, reproject); function reproject() { var c = projection.rotate().slice(0,2).map(d => -d) d3.selectAll("circle") .attr("transform", function(d) { return "translate(" + projection([d.lon,d.lat]) + ")";}) .attr("opacity", function(d){ // clipAngle(90) return d3.geoDistance([+d.lon,+d.lat], c) < Math.PI/2 ? 1 : 0; }) d3.selectAll(".world") .attr("d", path); } </script>