D3_Mapping_On_Canvas =
{
for (const g in d3Geo) {
d3[g] = d3Geo[g];
}
for (const g in d3GeoProj) {
d3[g] = d3GeoProj[g];
}
var width = 1000,
height = 600,
airportMap;
function speed(x) {
x = x > 99 ? 99 : x;
x = x < 1 ? 1 : x;
return -x + 100;
}
var projection = d3.geoRobinson()
.scale(180)
.translate([width / 2, height / 2]);
var path = d3.geoPath()
.projection(projection)
.pointRadius(1);
var svg = d3.select(model)
.attr('width', width)
.attr('height', height);
function transitionPath(plane, routeLength, wayPoints) {
plane.transition()
.duration(routeLength * speed(40))
.attrTween('transform', interpolatePoints(wayPoints))
.on('end', function() { transitionPath(plane, routeLength, wayPoints); });
}
function interpolatePoints(wayPoints) {
return function() {
return function(t) {
var index = Math.floor(t * (wayPoints.length-1));
return 'translate(' + wayPoints[index].x + ', ' + wayPoints[index].y + ')'
};
};
}
var planeModule = {
planes: [],
planeRoutes: [],
getAllRoutes: function(routes) {
var allRoutes = [];
routes.forEach(function(el) {
var arr = [el.source_airport, el.destination_airport];
allRoutes.push(arr);
});
this.planeRoutes = allRoutes;
},
getPlane: function() {
var planeCircle = svg.append('path')
.attr('class', 'plane')
.attr('d', 'M-1,0a1,1 0 1,0 2,0a1,1 0 1,0 -2,0');
return planeCircle;
},
getRoute: function(route) {
var origin = route[0];
var destination = route[1];
var routePath = svg.append('path')
.datum({
type: 'LineString',
coordinates: [
airportMap.get(origin).geometry.coordinates,
airportMap.get(destination).geometry.coordinates
]
})
.attr('class', 'route')
.attr('d', path);
var routeLength = routePath.node().getTotalLength()
return {
routePath: routePath,
routeLength: routeLength
};
},
getWayPoints: function(routeSvgPath) {
var planeWayPoints = [];
var path = routeSvgPath.node();
var totalPoints = Math.floor(path.getTotalLength() * 2.5);
d3.range(totalPoints).forEach(function(el, i) {
var DOMPoints = path.getPointAtLength(i/2.5);
planeWayPoints.push({ x: DOMPoints.x, y: DOMPoints.y });
});
return planeWayPoints;
},
makePlane: function(routes) {
this.getAllRoutes(routes);
var that = this;
this.planeRoutes.forEach(function(el) {
var plane = that.getPlane();
var routePath = that.getRoute(el).routePath;
var routeLength = that.getRoute(el).routeLength;
var wayPoints = that.getWayPoints(routePath);
if (wayPoints.length > 1) {
that.planes.push({
plane: plane,
routeLength: routeLength,
wayPoints: wayPoints
});
}
});
}
};
function drawMap(world) {
var mapData = topojson.feature(world, world.objects.countries).features;
svg.append('g')
.attr('class', 'countries')
.selectAll('path')
.data(mapData)
.enter()
.append('path')
.attr('d', path);
}
function drawAirports(airports) {
svg.append('g')
.attr('class', 'airports')
.selectAll('path')
.data(airports)
.enter()
.append('path')
.attr('id', function(d) { return d.id; })
.attr('d', path);
}
function ready(routes, airports) {
var airportLocation = [];
airports.forEach(function(el) {
var obj = {};
obj.type = 'Feature';
obj.id = el.iata;
obj.geometry = {
type: 'Point',
coordinates: [+el.long, +el.lat]
};
obj.properties = {};
airportLocation.push(obj);
});
airportMap = d3.map(airportLocation, function(d) { return d.id; });
drawAirports(airportLocation);
var routeFromTo = [];
routes.forEach(function(el) {
var arr = [el.source_airport, el.destination_airport];
routeFromTo.push(arr);
});
planeModule.makePlane(routes);
planeModule.planes.forEach(function(el) {
transitionPath(el.plane, el.routeLength, el.wayPoints);
});
}
d3.json('https://raw.githubusercontent.com/larsvers/Learning-D3.js-4-Mapping/master/chapter-9/data/countries.topo.json').then(function(world) {
drawMap(world);
setInterval(function () {
console.log('Drawing flight paths...');
d3.selectAll('.airports, .route, .plane').remove();
var flights = 100;
var routes = d3.csv('https://raw.githubusercontent.com/larsvers/Learning-D3.js-4-Mapping/master/chapter-9/data/routes_' + flights + '.csv').then(function(routes){ return routes; });
var airports = d3.csv('https://raw.githubusercontent.com/larsvers/Learning-D3.js-4-Mapping/master/chapter-9/data/airports_' + flights + '.csv').then(function(airports){ return airports; });
Promise.all([routes, airports]).then(function(response) {
var routesResolved = response[0];
var airportsResolved = response[1];
ready(routesResolved, airportsResolved)
});
}, 33000);
});
var stats=new Stats();
stats.dom.setAttribute('style', stats.dom.getAttribute('style').replace('fixed', 'absolute').replace('top', 'bottom'));
context.canvas.parentNode.appendChild(stats.dom);
requestAnimationFrame(function loop(){
stats.update();
requestAnimationFrame(loop);
});
return svg.node();
}