Flight Routes
Vamos a usar mapas en conjunto con datos geográficos externos (no mapas) para crear una visualización de las rutas aéreas más importantes. Comenzaremos dibujando el mapa usando nuestro mapa reusable, esta vez usando el formato TopoJSON.
// Creamos y configuramos el mapa reusable
var map = geojsonMap().width(800).height(400).scale(800 / (2 * Math.PI));
// Cargamos los datos en formato topojson
d3.json('/src/data/countries.topojson', function(error, data) {
if (error) { console.error(error); }
// Convierte los datos a GeoJSON
geojson = topojson.feature(data, data.objects.countries);
d3.select('#ejemplo-a01')
.data([geojson.features])
.call(map);
});
Aeropuertos
Nuestro archivo archivo airports.dat que contiene información de los aeropuertos en formato CSV. Hay casi 7,000 aeropuertos en el archivo, cada uno tiene códigos, el nombre de la ciudad en que está y su ubicación geográfica.
// AirportId,Name,City,Country,IATA,ICAO,Lat,Lon,Alt,TimeZone,DST
// 1,"Goroka","Goroka","Papua New Guinea","GKA","AYGA",-6.081689,145.391881,5282,10,"U"
// 2,"Madang","Madang","Papua New Guinea","MAG","AYMD",-5.207083,145.7887,20,10,"U"
// 3,"Mount Hagen","Mount Hagen","Papua New Guinea","HGU","AYMH",-5.826789,144.295861,5388,10,"U"
// 4,"Nadzab","Nadzab","Papua New Guinea","LAE","AYNZ",-6.569828,146.726242,239,10,"U"
// 5,"Port Moresby Jacksons Intl","Port Moresby","Papua New Guinea","POM","AYPY",-9.443383,147.22005,146,10,"U"
Vamos a cargar los datos usando d3.csv
, aprovechando de calcular la proyección de cada aeropuerto. Guardaremos las coordenadas (en pixeles) en el attributo pixels
de cada ítem de datos.
d3.csv('/src/data/airports.dat', function(error, data) {
if (error) { console.error(error); }
var div = d3.select('#ejemplo-a01'),
svg = div.selectAll('svg').data([data]);
var projection = map.projection();
// Precalculamos la proyección del aeropuerto
data.forEach(function(d) {
d.pixels = projection([+d.Lon, +d.Lat]);
});
// Agregamos un círculo para cada aeropuerto
var circles = svg.selectAll('circle.airport').data(data);
circles.enter().append('circle')
.classed('airport', true);
circles
.attr('cx', function(d) { return d.pixels[0]; })
.attr('cy', function(d) { return d.pixels[1]; })
.attr('r', 1);
circles.exit().remove();
});
Graficando las Rutas
El archivo routes.dat, que tiene información de las routas aéreas en formato CSV, en total son cerca de 60,000 rutas. Cada línea del archivo contiene información del aeropuerto de orígen y de destino, pero no las coordenadas de cada aeropuerto. Para obtenerlas, necesitaremos juntar información de ambos archivos.
// Airline,AirlineId,Source,SourceId,Target,TargetId,CodeShare,Stops,Equipment
// #C7,\N,MAO,2551,CIZ,7398,,0,EMB
// #C7,\N,MAO,2551,MBZ,7396,,0,EMB
// #C7,\N,MAO,2551,MNX,2594,,0,EMB
// #C7,\N,MNX,2594,RBB,7397,,0,EMB
Graficaremos los países una vez más para usar este gráfico como base.
// Creamos y configuramos el mapa reusable
var map = geojsonMap().width(800).height(400).scale(800 / (2 * Math.PI)),
projection = map.projection();
// Cargamos los datos en formato topojson
d3.json('/src/data/countries.topojson', function(error, data) {
if (error) { console.error(error); }
// Convierte los datos a GeoJSON
geojson = topojson.feature(data, data.objects.countries);
d3.select('#ejemplo-b01')
.data([geojson.features])
.call(map);
});
Ahora necesitamos cargar dos archivos de datos, y agregar las coordenadas de los aeropuertos de orígen y destino a cada ruta. Cargaremos ambos archivos por separado, cada uno con su propia llamada a d3.csv
.
// Guardaremos los aeropuertos y rutas en variables globales
var airports = {};
var routes;
// Marcaremos estas variables como true al cargar cada set de datos
var airportsReady = false,
routesReady = false;
// Cargamos los datos de aeropuertos
d3.csv('/src/data/airports.dat', function(error, data) {
if (error) { console.error(error); }
// Precalcula la proyección de las coordenadas del aeropuerto
data.forEach(function(d) {
d.pixels = projection([+d.Lon, +d.Lat]);
airports[d.AirportId] = d;
});
airportsReady = true;
// Si ambos archivos están listos, juntar los datos y dibujar
if ((airportsReady) && (routesReady)) {
joinData();
renderLines();
}
});
d3.csv('/src/data/routes.dat', function(error, data) {
if (error) { console.error(error); }
routes = data;
routesReady = true;
if ((airportsReady) && (routesReady)) {
joinData();
renderLines();
}
});
function joinData() {
// Agrega referencias a los aeropuertos de origen y destino
routes.forEach(function(route) {
route.sourcePixels = airports[route.SourceId] ? airports[route.SourceId].pixels : false;
route.targetPixels = airports[route.TargetId] ? airports[route.TargetId].pixels : false;
});
// Filtra las rutas, por si algún aeropuerto no fue encontrado
routes = routes.filter(function(d) {
return d.sourcePixels && d.targetPixels;
});
}
function renderLines() {
var div = d3.select('#ejemplo-b01'),
svg = div.selectAll('svg').data([routes]);
var lines = svg.selectAll('line.route')
.data(routes);
lines.enter().append('line')
.classed('route', true);
lines
.attr('x1', function(d) { return d.sourcePixels[0]; })
.attr('y1', function(d) { return d.sourcePixels[1]; })
.attr('x2', function(d) { return d.targetPixels[0]; })
.attr('y2', function(d) { return d.targetPixels[1]; });
lines.exit().remove();
}