Mapas — d3-geo

Para generar mapas en D3 seguimos los siguientes pasos:

Generamos un objeto path con la función d3.geoPath() añadiéndole una proyección de las tantas existentes en d3-geo. Un ejemplo sería:

var path = d3.geoPath().projection(geoAzimuthalEquidistant());

O también:

var projection = geoAzimuthalEquidistant(),
    path = d3.geoPath(projection);

Podemos optar por renderizar el mapa con svg o con canvas, vamos a explorar ambas opciones.

SVG

Obtenemos el elemento svg donde queremos representar el mapa. A este le añadimos los datos con data() o datum() y mediante el atributo "d" insertamos el path que hemos creado anteriormente:

<svg width="100%" height="500" id="example_map"></svg>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.13.0/d3.min.js"></script>
<script src="https://unpkg.com/topojson-client@3"></script>

<script>
  var url = "https://unpkg.com/world-atlas@1.1.4/world/110m.json";
  d3.json(url, function(error, world){
      if (error) throw error;

      var svg = d3.select("#example_map")
        .datum(topojson.feature(world, world.objects.land))
        .attr("d", path);
  })
</script>

Nota

Lee este artículo para entender la diferencia entre data() o datum().

Input

<style>
#map-1 {
  background-color: skyblue;
}
.province {
  fill: royalblue;
  stroke: #FFF;
  stroke-width: .5px;
  stroke-dasharray: 3 3;
}

.province:hover {
  fill: crimson;
  stroke: orange;
  stroke-width: 2px;
  stroke-dasharray: none;

}

.graticule {
  fill: none;
  stroke: #FFF;
  stroke-width: .6px;
  stroke-opacity: 0.5;
}
</style>

<svg id="map-1" width="748" height="500"></svg>

<script>
  var svg = d3.select("#map-1"),
      width = +svg.attr("width"),
      height = +svg.attr("height");

  // Creamos la proyección  (ver Proyecciones abajo)
  var projection = d3.geoMercator()
    .scale(2200)
    .center([0, 40])
    .translate([width / 1.7, height / 2]);
    //.translate([350, 200]);         // Otros atributos
    //.rotate([122.4194, -37.7749])
    //.clipAngle(180 - 1e-3)
    //.precision(0.1);

  // Creamos el path añadiendo la proyección
  var path = d3.geoPath(projection),

  // Creamos una rejilla que se repita cada 2 grados tanto
  //   en direcciones norte-sur como este-oeste
  var graticule = d3.geoGraticule().step([2, 2]);

  // Añadimos la rejilla
  svg.append("path")
      .datum(graticule)
      .attr("class", "graticule")
      .attr("d", path);

  // Obtenemos las provincias de España en formato geojson
  var url = "https://raw.githubusercontent.com/codeforamerica/click_that_hood/master/public/data/spain-provinces.geojson";
  d3.json(url, function(error, spain){
      if (error) throw error;  // Manejamos cualquier posible error

      var group = svg.selectAll("g")  // Creamos un grupo para cada provincia
          .data(spain.features)
          .enter()
          .append("g");

      // Para cada grupo añadimos el path correspondiente
      var areas = group.append("path")
          .attr("d", path)
          .attr("class", "province");

  });
</script>

Output


Como puedes observar en este ejemplo, básicamente son 6 pasos.

  1. Crear una proyección (ver Proyecciones).
  2. Crear un path con la función d3.geoPath() y añadirle la proyección.
  3. [Opcional] Crear una rejilla con la función d3.geoGraticule(). Añadir un elemento path al svg, enlazar los datos de la cuadrícula con la función datum() y añadir el path del paso anterior al atributo d.
  4. Obtener los datos geográficos del mapa.
  5. Enlazar los datos al contenedor svg por medio de grupos.
  6. Añadir a cada grupo un elemento path, cuyo atributo d será el path que hemos creado en el paso 2.

Canvas

Para crear un mapa usando elementos canvas habría que escribir algo como esto:

Input

<canvas width="680" width="480"></canvas>

<script src="https://unpkg.com/topojson-client@3"></script>
<script>

  var context = d3.select("canvas").node().getContext("2d");

  var projection = d3.geoMercator()
    .scale(2200)
    .center([0, 40])
    .translate([width / 1.7, height / 2]);

  var path = d3.geoPath(projection).context(context);

  var url = "https://raw.githubusercontent.com/codeforamerica/click_that_hood/master/public/data/spain-provinces.geojson";
  d3.json(url, function(error, spain) {
      if (error) throw error;

      // Comenzamos a dibujar en el lienzo
      context.beginPath();

      // Añadimos el path con los datos del archivo .json
      path(topojson.mesh(spain));

      // Dibujamos el contenido
      context.stroke();
  });

</script>

Output

En este ejemplo, mucho más simple, hemos usado topojson para cargar los datos del archivo en json e insetarlos en el canvas.

Proyecciones

D3 provee muchas proyecciones dentro de los módulos d3-geo y d3-geo-projection. Cada proyección puede ser controlada mediante métodos.

Métodos

  • projection.scale([scale]): El factor de escalado corresponde linealmente a la distancia entre los puntos projectados; sin embargo, mismos factores de escalado no son equivalentes entre diferentes proyecciones.
  • projection.center([center]): Array de dos elementos con las coordenadas longitud y latitud en grados (por defecto [0, 0]).
  • projection.translate([translate]): si se especifica el parámetro translate, el la proyección será trasladada en los ejes [x, y] según los valores introducidos (por defecto [480, 250]).
  • projection.rotate([angles]): Si él parámetro angles es introducido, establece la rotación esférica sobre los tres ejes a los ángulos especificado, el cual debe ser un array de dos ó tres números [lambda, phi, gamma] , especificando los ángulos de rotación en grados sobre cada eje esférico (por defecto [0, 0, 0].

Rejillas

d3.geoGraticule()

Con esta función creamos una rejilla. Para añadir una rejilla simplemente hemos de insertarla en el contenedor svg con la función datum():

var graticule = d3.geoGraticule().step([2, 2]);

svg.append("path")
    .datum(graticule)
    .attr("d", path);

Métodos

  • graticule.step([step]): Acepta un array de dos números que indican los grados de distancia entre cada filamento de la rejilla. El primero indica diferencia entre longitudes y el segundo latitudes.