Creating the Earth with D3.js

There is no way around it, creating data visualizations with D3 is just plain fun. Lately, I have especially enjoyed building map visualizations, but I hadn’t figured out an excuse to use the azimuthal projection on a project.

I still haven’t, but this Earth is pretty cool looking:

Click and drag to pan, use scroll wheel to zoom Fullscreen

There are really three parts working here:

  1. The actual map, which is an orthographic azimuthal projection of the countries of the world onto the outside of a sphere.
  2. The stars and outer space, which is a equidistant azimuthal projection onto the inside of a larger sphere
  3. A giant circle with the same diameter as the map sphere for background gradient and a Gaussian blur (SVG Filter)

Map

The map itself is pretty simple, each country is a GeoJSON ‘feature’ polygon which is just a list of lat/lon points that form the borders of the country:

{
  "type":"Feature",
  "properties":{
    "name":"Cyprus"
  },
  "geometry":{
    "type":"Polygon",
    "coordinates":[
      [
        [33.973617,35.058506],
        [34.004881,34.978098],
        [32.979827,34.571869],
        [32.490296,34.701655],
        [32.256667,35.103232],
        [32.73178,35.140026],
        [32.919572,35.087833],
        [33.190977,35.173125],
        [33.383833,35.162712],
        [33.455922,35.101424],
        [33.475817,35.000345],
        [33.525685,35.038688],
        [33.675392,35.017863],
        [33.86644,35.093595],
        [33.973617,35.058506]
      ]
    ]
  },
  "id":"CYP"
}

Each country is mapped to a spot on the page according to the projection, and the paths are clipped so that only the ones on the visible side of the sphere are shown.

Stars

Outer space is the inside of a sphere that is three times the size of the Earth itself. It is also another map projection, but instead of countries, there are 300 randomly placed points of varying sizes. These points are supposed to look like stars and create the backdrop for the Earth.

Polish

In order to make this look more like the Earth, there is a single SVG circle element behind all of the map data. The circle uses a SVG linearGradient and filter for a little bit of a 3D spherical look and the fuzzily defined edge caused by the atmosphere.

<svg id="defs">
    <defs>
        <linearGradient id="gradBlue" x1="0%" y1="0%" x2="100%" y2="0%">
            <stop offset="0%" style="stop-color:#005C99;stop-opacity:1" />
            <stop offset="100%" style="stop-color:#0099FF;stop-opacity:1" />
        </linearGradient>
        <filter id="glow">
            <feColorMatrix type="matrix"
                values=
                "0 0 0 0   0
                 0 0 0 0.9 0
                 0 0 0 0.9 0
                 0 0 0 1   0"/>
            <feGaussianBlur stdDeviation="5.5" result="coloredBlur"/>
            <feMerge>
                <feMergeNode in="coloredBlur"/>
                <feMergeNode in="SourceGraphic"/>
            </feMerge>
        </filter>
    </defs>
</svg>

Moving Around

D3 makes panning and zooming pretty easy to figure out with maps, however it is mostly tuned for the more often used projections like Albers and Mercator, which use the translate property instead of the origin to control the focal point. That works great for those projections because it allows the scrollwheel to zoom in on the mouse pointer as it does in Google Maps.

Unfortunately, that paradigm doesn’t work as well for this azimuthal projection, so we need to detach the pan and zoom events from affecting each other. This isn’t currently an option in D3, so I have a pull request with a fix that will hopefully be merged soon. Until then, I am using a custom version with my change already merged.

Now that panning and zooming are separated, the move function will calculate the origin and scale based on the mouse event and change the projections for the map and outer space accordingly. Then, the map will be redrawn using the new projections. This creates a smooth experience that allows your to pan and zoom around the Earth.

Feel free to play with the demo above and look through the code below or fork it on GitHub.

6 thoughts on “Creating the Earth with D3.js

  1. I am using your experimental globe for a project. I am drawing dots onto the globe, got that all working fine. The only problem I am facing is that when zoomed in, the map pans too fast. The pan distance does not scale as the user zooms in. Do you have any suggestions on how to compensate for the zoom? I tried messing around with the origin in move(), which fixed it, but then the globe would rotate as I zoomed in.

  2. Yes, I am using you custom version of d3. I turned off all of the fancy stuff in the svg in the .html file, but otherwise it’s the same. I looked at that pull request, but it seems like you made those changes in your custom version. I still have the problem though, maybe I am doing something wrong. Thanks for the quick response!

  3. Sorry, I misread your comment before and I see your problem now. I will look into it and see if I can figure how to adjust that

  4. Hey Eric, sorry for never getting back to you. I tried for a while to find a workaround, but was never able to figure out. I don’t think you will be able to make it work correctly without rewriting d3’s zoom behavior.

Comments are closed.