Chapter 11. Creating Advanced Maps

In the last chapter, we learned how to use the GeoJSON and TopoJSON formats to create map-based charts using SVG. In this chapter, we will explore different cartographic projections and learn how to use the Orthographic and Stereographic projections to create 3D-like renderings of our maps.

We will add interaction to our maps by adding drag and zoom behavior, allowing the user to rotate and zoom the map views. We will use the Orthographic projection to create a rotating view of the Earth, and we will create a star map using the Orthographic projection.

We will also learn how to project raster images of the Earth using canvas and D3 projections in order to have realistic renderings of it.

Using cartographic projections

As we mentioned in the previous chapter, cartographic projections are functions that map positions on the Earth to points on a flat surface. The d3.geo module of D3 implements about a dozen of the most used cartographic projections, and there are even more cartographic projections available in the extended geographic projections plugin at https://github.com/d3/d3-geo-projection/.

There are a great number of projections because none of them are appropriate for every application. The Mercator projection, for instance, was created as a navigation tool. Straight lines on the Mercator projection are rhumb lines, which are lines of constant compass bearing. This projection is very useful for navigation, but the areas near the poles are extremely distorted. The poles, which are points on the surface of the Earth, are represented as lines that are as long as the equator. The Orthographic projection, on the other hand, is closer to what we would see from space, creating a more accurate mental image of how the Earth really is, but it's probably not very useful to navigate by sea.

In this section, we will learn how to use more projections and discuss some of their properties. The examples in this section are in the chapter11/01-projections file in the code bundle. For the examples in this section, we will use a TopoJSON file that contains land features, which are generated from the medium-scale shapefiles from Natural Earth. The Makefile in the chapter11/data folder will download and transform the necessary files for us.

Using the Equirectangular projection

The Equirectangular projection linearly maps longitude to a horizontal position and latitude to a vertical position using the same scale. As there are 180 degrees from pole to pole, and the circumference of the earth covers 360 degrees, the width of a world map created with this projection will be twice its height. It's mathematical simplicity is about its only useful property.

To create a world map, we begin by loading the TopoJSON file and using the topojson.feature method to compute the GeoJSON object, representing the shapes described in the ne_50m_land object:

d3.json('/chapter11/data/land.json', function(error, data) {

    // Notifies about errors getting or parsing the data
    if (error) { console.error(error); }

    // Construct the GeoJSON features
    var geojson = topojson.feature(data, data.objects.ne_50m_land);

    // Create the projection and draw the features...
});

As usual, we set the width and height of the svg container of the map. We select the container div for the map and append the svg element, setting its width and height:

    // Set the width and height of the svg element
    var width = 600, 
        height = 300;

    // Append the svg container and set its size
    var div = d3.select('#map-equirectangular'),
        svg = div.append('svg')
            .attr('width', width)
            .attr('height', height);

We create an instance of the d3.geo.equirectangular projection and configure its scale and translation by chaining the corresponding methods. Note that scales are not consistent among projections. In this case, in order to show the world map, we can set the scale either to height / Math.PI or width / (2 * Math.PI). In both cases, the result will be the same:

    // Create an instance of the Equirectangular projection
    var equirectangular = d3.geo.equirectangular()
        .scale(width / (2 * Math.PI))
        .translate([width / 2, height / 2]);

Once we have the projection created, we can create an instance of the geographic path generator and set its projection attribute:

    // Create and configure the geographic path generator
    var path = d3.geo.path()
        .projection(equirectangular);

The path generator receives a GeoJSON feature or feature collection and uses the projection to compute the corresponding svg path string. Finally, we append a path element to the svg container, bind the feature collection to the selection, and set the path data string to be computed with the path generator:

    // Append the path of the features to the svg container
    svg.append('path').datum(geojson)
        .attr('class', 'land')
        .attr('d', path);

We will also add parallels and meridians to the figure. D3 has a generator that creates these lines. The d3.geo.graticule() method returns a configurable function that creates a feature collection that contains the graticule lines:

    // Create the graticule lines
    var graticule = d3.geo.graticule();

    // Add the graticule to the figure
    svg.append('path').datum(graticule())
        .attr('class', 'graticule')
        .attr('d', path);

A world map created with the Equirectangular projection is shown in the following screenshot:

Using the Equirectangular projection

The Conic Equidistant projection

The Conic Equidistant projection maps the sphere into a cone whose axis coincides with the axis of the Earth. The cone can be tangent to the sphere in one parallel or secant in two parallels, which are called standard parallels. In this projection, the poles are represented with arcs, and the local shapes are true among the standard parallels. In the Conic Equidistant projection, the distances among meridians are proportionally correct. They are better suited to represent regions that have a small range of latitude, such as regional maps or small countries.

The process to generate a world map with this projection is the same as the previous process, except that this time, we need to create and configure an instance of the d3.geo.conicEquidistant projection. For this projection, computing the exact scale will be more difficult, but it's easy to adjust it until it has the correct size:

    // Create and configure an instance of the projection
    var conic = d3.geo.conicEquidistant()
        .scale(0.75 * width / (2 * Math.PI))
        .translate([width / 2, height / 2]);

The world map created with the Conic Equidistant projection is shown in the following screenshot:

The Conic Equidistant projection

As we mentioned earlier, this projection is not appropriate to display the world map, but it can represent small areas accurately. We can rotate the projection to center it around New Zealand and set a bigger scale. We set the standard parallels to 5 degrees north and 15 degrees south. This will minimize the distortion among these parallels:

    // Create and configure an instance of the projection
    var conic = d3.geo.conicEquidistant()
        .scale(0.85 * width / (Math.PI / 3))
        .rotate([-141, 0])
        .translate([width / 2, height / 2])
        .parallels([5, -15]);

The map of New Zealand using the Conic Equidistant projection is shown in the following screenshot:

The Conic Equidistant projection

The Orthographic projection

The Orthographic projection is a perspective projection that shows the Earth as seen from space. This gives us the illusion of a three-dimensional view. Only one hemisphere can be seen at a time without overlapping. This has minimal distortion near the center and huge distortion towards the horizon.

To use this projection, we need to set the scale and translation of the projection and use it to configure the path generator:

    // Create an instance of the Orthographic projection
    var orthographic = d3.geo.orthographic()
        .scale(height / 2)
        .translate([width / 2, height / 2]);

An orthographic view of the Earth is shown in the following screenshot:

The Orthographic projection

To avoid overlapping, we would need to display only the features that are on the same side as the observer. To do this, we need to clip the projection to hide the features that are at the other side of the Earth. The clipAngle method allows us to clip the features beyond the clipping angle. Setting this angle to 90 will modify the geometry of the features whose angular distance is greater than 90 from the center of the projection; so, they are not shown in the image:

    // Create an instance of the Orthographic projection
    var orthographic = d3.geo.orthographic()
        .scale(height / 2)
        .translate([width / 2, height / 2])
        .clipAngle(90);

The following screenshot shows us Earth from the side of the observer:

The Orthographic projection

An Orthographic view of Earth with clipping

The projections shown in this section are only a small sample of the projections available in the geographic module of D3. As we can see, the pattern of use of different projections is always the same, but the parameters of each projection should be adjusted to get the desired result. In the next section, we will learn how to use the drag behavior to rotate the globe.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset