xxxxxxxxxx
<meta charset='utf-8'>
<style>
.baseMap{
stroke-width:0.8px;
stroke:white;
fill:#E4E5E6;
opacity:0.9;
}
.cities_start{
fill:rgba(199,70,70,.8);
/* fill:none;*/
}
.cities_end{
fill:rgba(29, 168, 183, 1);
/* fill:none;*/
}
.line{
/* stroke:rgba(0, 0, 0, 0.3);*/
stroke:rgba(199,70,70,.7);
stroke-width:3px;
fill:none;
stroke-dasharray:3, 3;
}
.geo-globe {
fill: rgba(236,249,255,0.8);
/* fill:white;*/
}
</style>
<body>
<script src='https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.5/d3.min.js'></script>
<script src='https://cdnjs.cloudflare.com/ajax/libs/queue-async/1.0.7/queue.min.js'></script>
<script src='https://cdnjs.cloudflare.com/ajax/libs/topojson/1.6.19/topojson.min.js'></script>
<script src='arc.js'></script>
<div id='map'>
<script>
////////////////////////////////
////////////////////////////////
// preparation: svg's width/height, projection, path, voronoi
////////////////////////////////
////////////////////////////////
// I followed Mike Bostock's margin convention to set margins first,
// and then set the width and height based on margins.
// Here's the link to the margin convention
// https://bl.ocks.org/mbostock/3019563
var margin = {top: 30, right: 30, bottom: 30, left: 30},
width = 800 - margin.left - margin.right,
height = 600 - margin.top - margin.bottom;
// This is the projection for the flat map
// var projection = d3.geo.mercator()
// // .center([121.0, 23.5])
// .translate([width / 2, height / 1.5])
// .scale(125); // feel free to tweak the number for scale and see the changes
//This is the project for the globe
var projection = d3.geo.orthographic().scale(280).translate([400,300]).clipAngle(90).precision(0.5);
var path = d3.geo.path()
.projection(projection);
// Create a voronoi layer for better mouse interaction experience
// For more reading on voronoi, check out
// https://www.visualcinnamon.com/2015/07/voronoi.html
var voronoi = d3.geom.voronoi()
.x(function(d) { return d.x; })
.y(function(d) { return d.y; })
.clipExtent([[0, 0], [width, height]]);
var svg = d3.select('#map').append('svg')
.attr('width', width + margin.left + margin.right)
.attr('height', height + margin.top + margin.bottom)
.attr('class', 'graph-svg-component')
.call(responsivefy)// Call function responsivefy to make the graphic reponsive according to the window width/height
.append('g')
.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
var backgroundCircle = svg.append("circle")
.attr('cx', width / 1.85)
.attr('cy', height / 1.84099)
.attr('r', 0)
.attr('class', 'geo-globe');
backgroundCircle.attr('r', projection.scale());
////////////////////////////////
////////////////////////////////
// Queue: queue is an asynchronous helper library for JavaScrip
// It helps coders to easily load multiple datasets
// Here's the link to queue github repository:
// https://github.com/mbostock/queue
////////////////////////////////
////////////////////////////////
queue()
.defer(d3.json, 'world_countries.json')// load geojson/topojson data
.defer(d3.csv, 'data.csv')
// .defer(d3.csv, 'flights.csv')
.await(ready);
function ready(error, world, data) {
if (error) throw error;
data.forEach(
function(d){
d.end_lat = +d.end_lat;
d.end_long = +d.end_long;
d.start_lat = +d.start_lat;
d.start_long = +d.start_long;
d.greatcircle = new arc.GreatCircle({x:d.start_long, y:d.start_lat}, {x:d.end_long, y:d.end_lat});
d.line = d.greatcircle.Arc(100, {offset:10});
d.arc = d.line.json();
}
);
svg.selectAll('path')
.data(world.features)
.enter()
.append('path')
.attr('d', path)
// .append("g")
.attr('class','baseMap');
svg.selectAll('.cities_start')
.data(data)
.enter()
.append('circle')
.attr('cx', function(d){ return projection([d.start_long, d.start_lat])[0]})
.attr('cy', function(d){ return projection([d.start_long, d.start_lat])[1]})
.attr('r', '3')
// .append("g")
.attr('class','cities_start');
svg.selectAll('.cities_end')
.data(data)
.enter()
.append('circle')
.attr('cx', function(d){ return projection([d.end_long, d.end_lat])[0]})
.attr('cy', function(d){ return projection([d.end_long, d.end_lat])[1]})
.attr('r', '3')
// .append("g")
.attr('class','cities_end');
svg.append("g")
.attr("class", "line")
.selectAll(".arcs")
.data(data.map(function(d){ return d.arc; }))
.enter()
.append("path")
.attr("d", path);
}
d3.select("svg").call( //drag on the svg element
d3.behavior.drag()
.origin(function() {
var r = projection.rotate();
return {x: r[0], y: -r[1]}; //starting point
})
.on("drag", function() {
var r = projection.rotate();
/* update retation angle */
projection.rotate([d3.event.x, -d3.event.y, r[2]]);
/* redraw the map and circles after rotation */
svg.selectAll("path").attr("d",path);
svg.selectAll(".cities_start")
.attr('cx', function(d){ return projection([d.start_long, d.start_lat])[0]})
.attr('cy', function(d){ return projection([d.start_long, d.start_lat])[1]})
svg.selectAll(".cities_end")
.attr('cx', function(d){ return projection([d.end_long, d.end_lat])[0]})
.attr('cy', function(d){ return projection([d.end_long, d.end_lat])[1]})
})
);
function responsivefy(svg) {
var container = d3.select(svg.node().parentNode),
width = parseInt(svg.style('width')),
height = parseInt(svg.style('height')),
aspect = width / height;
svg.attr('viewBox', '0 0 ' + width + ' ' + height)
.attr('perserveAspectRatio', 'xMinYMid')
.call(resize);
d3.select(window).on('resize', resize);
function resize() {
var targetWidth = parseInt(container.style('width'));
svg.attr('width', targetWidth);
svg.attr('height', Math.round(targetWidth / aspect));
}
}
</script>
https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.5/d3.min.js
https://cdnjs.cloudflare.com/ajax/libs/queue-async/1.0.7/queue.min.js
https://cdnjs.cloudflare.com/ajax/libs/topojson/1.6.19/topojson.min.js