| east | north | elevation | magnitude | slab |
|---|---|---|---|---|
| 181.62 | -20.42 | -562 | 4.8 | 540-630 km |
| 181.03 | -20.62 | -650 | 4.2 | 630-720 km |
| 184.10 | -26.00 | -42 | 5.4 | 0-90 km |
| 181.66 | -17.97 | -626 | 4.1 | 540-630 km |
| 181.96 | -20.42 | -649 | 4.0 | 630-720 km |
36 Globe
What does the data look like on the earth itself? The map chapter flattened the sphere to fit a page. This chapter keeps it round, and turns it toward you.
Here is the map chapter’s first sentence, and beside it the same sentence with the space swapped:
(data(quakes_fiji) + point + x(east) + y(north) + map() +
title("map()")) |
(data(quakes_fiji) + point + x(east) + y(north) +
globe(turn = 178, tilt = -18) + title("globe(turn = 178, tilt = -18)"))((data(quakes_fiji) + point + x(col.east) + y(col.north) + map() +
title("map()")) |
(data(quakes_fiji) + point + x(col.east) + y(col.north) +
globe(turn = 178, tilt = -18) + title("globe(turn = 178, tilt = -18)")))(data(quakes_fiji) + point + x(:east) + y(:north) + map() +
title("map()")) |
(data(quakes_fiji) + point + x(:east) + y(:north) +
globe(turn = 178, tilt = -18) + title("globe(turn = 178, tilt = -18)"))beside(plot(data(quakes_fiji), point, x(col.east), y(col.north), map(),
title("map()")),
plot(data(quakes_fiji), point, x(col.east), y(col.north),
globe({ turn: 178, tilt: -18 }),
title("globe(turn = 178, tilt = -18)")))“Given quakes Fiji: points, x is east, y is north, on the globe.”
x is longitude and y is latitude, exactly as on the map. What changed is what a position means on the page. Each point now stands at its place on the sphere, and the sphere is drawn as the disk you would see looking at it.
36.1 The view
A globe shows one half of the earth, so which half matters. turn and tilt name the place the view faces, in the words space() already uses: turn is the longitude at the center of the disk, and tilt is the latitude. The plot above faces Fiji. Leave them out and the view faces zero and zero, where the equator crosses the prime meridian.
turn is a bearing, so any number is a view: 190 and -170 face the same place. tilt is a latitude, and it stops at the poles.
In the web edition the written angle is only where the globe starts. Drag it and it turns, the way the cube in Space turns, and the far half of the earth comes around; reset returns to the view the sentence asked for. On paper a plot keeps its one view, which is why this chapter also draws some data at a second angle.
Half the earth faces away, and data on that half is hidden behind the sphere. gog counts what a view hides rather than dropping it in silence:
| lon | lat | country | continent | piece |
|---|---|---|---|---|
| 66.519 | 37.363 | Afghanistan | Asia | p001 |
| 69.196 | 37.151 | Afghanistan | Asia | p001 |
| 70.807 | 38.486 | Afghanistan | Asia | p001 |
| 71.845 | 36.738 | Afghanistan | Asia | p001 |
| 73.260 | 37.495 | Afghanistan | Asia | p001 |
data(world_borders) + path + x(lon) + y(lat) + group(piece) +
style(color = "#c9ccd6") +
data(quakes_fiji) + point + x(east) + y(north) +
style(color = "tomato", opacity = 0.35, size = 3) +
globe(turn = 178, tilt = -18) + title("The map chapter's layers, kept round")(data(world_borders) + path + x(col.lon) + y(col.lat) + group(col.piece) +
style(color = "#c9ccd6") +
data(quakes_fiji) + point + x(col.east) + y(col.north) +
style(color = "tomato", opacity = 0.35, size = 3) +
globe(turn = 178, tilt = -18) + title("The map chapter's layers, kept round"))data(world_borders) + path + x(:lon) + y(:lat) + group(:piece) +
style(color = "#c9ccd6") + data(quakes_fiji) + point + x(:east) +
y(:north) + style(color = "tomato", opacity = 0.35, size = 3) +
globe(turn = 178, tilt = -18) +
title("The map chapter's layers, kept round")plot(data(world_borders), path, x(col.lon), y(col.lat), group(col.piece),
style({ color: "#c9ccd6" }), data(quakes_fiji), point, x(col.east),
y(col.north), style({ color: "tomato", opacity: 0.35, size: 3 }),
globe({ turn: 178, tilt: -18 }),
title("The map chapter's layers, kept round"))gog: 3192 of 4150 row(s) face away from `globe(turn = 178, tilt = -18)` and are hidden behind the sphere. Turn to face them, or put a second view beside this one with `|`.
“Given the world borders: paths, x is lon, y is lat, grouped by piece; then given quakes Fiji: points, x is east, y is north, on the globe.”
Read the message above the plot. The coastline table covers the whole earth, so most of its rows face away from any one view, while every earthquake faces this one. Turning the globe brings the hidden rows around:
data(world_borders) + path + x(lon) + y(lat) + group(piece) +
style(color = "#c9ccd6") +
globe(turn = 20, tilt = 5) + title("The other side")(data(world_borders) + path + x(col.lon) + y(col.lat) + group(col.piece) +
style(color = "#c9ccd6") +
globe(turn = 20, tilt = 5) + title("The other side"))data(world_borders) + path + x(:lon) + y(:lat) + group(:piece) +
style(color = "#c9ccd6") + globe(turn = 20, tilt = 5) +
title("The other side")plot(data(world_borders), path, x(col.lon), y(col.lat), group(col.piece),
style({ color: "#c9ccd6" }), globe({ turn: 20, tilt: 5 }),
title("The other side"))36.2 The graticule
The gray lines on the sphere are the graticule: a meridian every thirty degrees of longitude, and a parallel every thirty degrees of latitude. They are this space’s gridlines, the same lines every panel draws, bent onto the sphere the way polar bends them into rings and spokes. There are no axes beside the disk, because a sphere has no edge to write one on; the graticule is the reference instead. theme(grid = ) reaches it like any other grid:
data(quakes_fiji) + point + x(east) + y(north) +
globe(turn = 178, tilt = -18) + theme(grid = "none") +
title("No graticule")(data(quakes_fiji) + point + x(col.east) + y(col.north) +
globe(turn = 178, tilt = -18) + theme(grid = "none") +
title("No graticule"))data(quakes_fiji) + point + x(:east) + y(:north) +
globe(turn = 178, tilt = -18) + theme(grid = "none") +
title("No graticule")plot(data(quakes_fiji), point, x(col.east), y(col.north),
globe({ turn: 178, tilt: -18 }), theme({ grid: "none" }),
title("No graticule"))36.3 A route bends with the sphere
On the map, path joins places with straight strokes. On the globe a straight route does not exist. The shortest way between two places bends with the surface, along a great circle, and a path here follows it:
| lon | lat |
|---|---|
| 139.69 | 35.69 |
| -118.24 | 34.05 |
data(world_borders) + path + x(lon) + y(lat) + group(piece) +
style(color = "#c9ccd6") +
data(flight) + path + x(lon) + y(lat) +
style(color = "#3d5a80", arrow = "end") +
globe(turn = 178, tilt = -18) + title("Tokyo to Los Angeles")(data(world_borders) + path + x(col.lon) + y(col.lat) + group(col.piece) +
style(color = "#c9ccd6") +
data(flight) + path + x(col.lon) + y(col.lat) +
style(color = "#3d5a80", arrow = "end") +
globe(turn = 178, tilt = -18) + title("Tokyo to Los Angeles"))data(world_borders) + path + x(:lon) + y(:lat) + group(:piece) +
style(color = "#c9ccd6") + data(flight) + path + x(:lon) + y(:lat) +
style(color = "#3d5a80", arrow = "end") +
globe(turn = 178, tilt = -18) + title("Tokyo to Los Angeles")plot(data(world_borders), path, x(col.lon), y(col.lat), group(col.piece),
style({ color: "#c9ccd6" }), data(flight), path, x(col.lon), y(col.lat),
style({ color: "#3d5a80", arrow: "end" }),
globe({ turn: 178, tilt: -18 }), title("Tokyo to Los Angeles"))Two rows describe that route, and the sphere sets the curve between them. The arc bends toward the pole because the shortest way across an ocean bends there too.
36.4 Many routes are a network
One table can hold every route at once. Give each route a name, give each of its two rows a place, and group() splits the table into one arc per route, exactly as it split the coastline into pieces. A point layer marks the cities and a text layer names them. The picture this draws is a route network, and it needs no new word, because every position in it is a place the data already knows:
| city | route | lon | lat |
|---|---|---|---|
| Seoul | Seoul-Tokyo | 126.98 | 37.57 |
| Seoul | Seoul-Singapore | 126.98 | 37.57 |
| Seoul | Seoul-Delhi | 126.98 | 37.57 |
| Tokyo | Seoul-Tokyo | 139.69 | 35.68 |
| Tokyo | Tokyo-Anchorage | 139.69 | 35.68 |
| city | lon | lat |
|---|---|---|
| Seoul | 126.98 | 37.57 |
| Tokyo | 139.69 | 35.68 |
| Singapore | 103.82 | 1.35 |
| Delhi | 77.21 | 28.61 |
| Cairo | 31.24 | 30.04 |
data(world_borders) + path + x(lon) + y(lat) + group(piece) +
style(color = "#c9ccd6") +
data(routes) + path + x(lon) + y(lat) + group(route) +
style(color = "#3d5a80", opacity = 0.6) +
data(cities) + point + x(lon) + y(lat) +
data(cities) + text + x(lon) + y(lat) + label(city) + style(nudge = "up") +
globe(turn = 95, tilt = 30) + title("Routes between cities")(data(world_borders) + path + x(col.lon) + y(col.lat) + group(col.piece) +
style(color = "#c9ccd6") +
data(routes) + path + x(col.lon) + y(col.lat) + group(col.route) +
style(color = "#3d5a80", opacity = 0.6) +
data(cities) + point + x(col.lon) + y(col.lat) +
data(cities) + text + x(col.lon) + y(col.lat) + label(col.city) + style(nudge = "up") +
globe(turn = 95, tilt = 30) + title("Routes between cities"))data(world_borders) + path + x(:lon) + y(:lat) + group(:piece) +
style(color = "#c9ccd6") + data(routes) + path + x(:lon) + y(:lat) +
group(:route) + style(color = "#3d5a80", opacity = 0.6) + data(cities) +
point + x(:lon) + y(:lat) + data(cities) + text + x(:lon) + y(:lat) +
label(:city) + style(nudge = "up") + globe(turn = 95, tilt = 30) +
title("Routes between cities")plot(data(world_borders), path, x(col.lon), y(col.lat), group(col.piece),
style({ color: "#c9ccd6" }), data(routes), path, x(col.lon), y(col.lat),
group(col.route), style({ color: "#3d5a80", opacity: 0.6 }),
data(cities), point, x(col.lon), y(col.lat), data(cities), text,
x(col.lon), y(col.lat), label(col.city), style({ nudge: "up" }),
globe({ turn: 95, tilt: 30 }), title("Routes between cities"))gog: 897 of 4150 row(s) face away from `globe(turn = 95, tilt = 30)` and are hidden behind the sphere. Turn to face them, or put a second view beside this one with `|`.
“Given the world borders: paths, x is lon, y is lat, grouped by piece; then given the routes: paths, x is lon, y is lat, grouped by route; then given the cities: points, x is lon, y is lat; then given the cities: text, x is lon, y is lat, label by city, on the globe.”
Looking up each city’s longitude and latitude is the host language’s job, the same join a later section does for country centers. What the sentence adds is only the layers: the routes, then the cities, then their names.
Every arc bends with the surface. The count above the plot reports what this view hides: the coastlines on the far side, and no city. Dragging the globe in the web edition brings those coastlines around. A network between places asks nothing new of the grammar. The places carry the positions, group() carries the connections, and the sphere carries the shortest ways between them.
36.5 Meridians and parallels
A rule draws a line at one position and spans the axis it does not name. On the map that drew the equator as a straight line. Here the same sentence bends it around the sphere:
| lat |
|---|
| 0 |
data(world_borders) + path + x(lon) + y(lat) + group(piece) +
style(color = "#c9ccd6") +
data(equator) + rule + y(lat) + style(color = "#d4674f") +
globe(turn = 20, tilt = 30) + title("The equator, bent around the sphere")(data(world_borders) + path + x(col.lon) + y(col.lat) + group(col.piece) +
style(color = "#c9ccd6") +
data(equator) + rule + y(col.lat) + style(color = "#d4674f") +
globe(turn = 20, tilt = 30) + title("The equator, bent around the sphere"))data(world_borders) + path + x(:lon) + y(:lat) + group(:piece) +
style(color = "#c9ccd6") + data(equator) + rule + y(:lat) +
style(color = "#d4674f") + globe(turn = 20, tilt = 30) +
title("The equator, bent around the sphere")plot(data(world_borders), path, x(col.lon), y(col.lat), group(col.piece),
style({ color: "#c9ccd6" }), data(equator), rule, y(col.lat),
style({ color: "#d4674f" }), globe({ turn: 20, tilt: 30 }),
title("The equator, bent around the sphere"))“Given the world borders: paths, x is lon, y is lat, grouped by piece; then given the lines of table: a rule, y is lat, on the globe.”
A rule on y is a parallel, a circle of constant latitude. A rule on x is a meridian, and it runs pole to pole.
36.6 The choropleth, kept round
Filling countries is the map’s most familiar use, and the edge of the disk is the hard case for a fill. The map chapter filled each country by its continent. The same sentence fills the countries on the sphere:
data(world_borders) + zone + x(lon) + y(lat) + group(country) +
color(continent) + globe(turn = 20, tilt = 5) +
title("Every country, filled by continent")(data(world_borders) + zone + x(col.lon) + y(col.lat) + group(col.country) +
color(col.continent) + globe(turn = 20, tilt = 5) +
title("Every country, filled by continent"))data(world_borders) + zone + x(:lon) + y(:lat) + group(:country) +
color(:continent) + globe(turn = 20, tilt = 5) +
title("Every country, filled by continent")plot(data(world_borders), zone, x(col.lon), y(col.lat),
group(col.country), color(col.continent), globe({ turn: 20, tilt: 5 }),
title("Every country, filled by continent"))“Given the world borders: zones, x is lon, y is lat, grouped by country, color by continent, on the globe.”
Look at the countries the edge of the disk cuts through. gog closes each cut shape along that edge, so a half-visible country is still a filled region rather than an open outline.
36.7 A measure stands on the radius
The flattened map has no axis to spare. The sphere has exactly one: the radius, which runs straight out from the surface at every place. A bar here stands at its place and measures outward along it, a spike from the surface, and z names its measure:
| country | lon | lat | people | continent | reach |
|---|---|---|---|---|---|
| Afghanistan | 67.366280 | 34.59696 | 31889923 | Asia | 5647.116 |
| Albania | 20.165857 | 41.23771 | 3600523 | Europe | 1897.504 |
| Algeria | 2.743348 | 29.27604 | 33333216 | Africa | 5773.493 |
| Angola | 16.961080 | -10.70456 | 12420476 | Africa | 3524.270 |
| Argentina | -65.436810 | -38.39190 | 40301927 | Americas | 6348.380 |
data(world_borders) + path + x(lon) + y(lat) + group(piece) +
style(color = "#c9ccd6") +
data(population_spikes) + bar + x(lon) + y(lat) + z(reach) + color(continent) +
globe(turn = 20, tilt = 12) +
title("World population, standing on its places")(data(world_borders) + path + x(col.lon) + y(col.lat) + group(col.piece) +
style(color = "#c9ccd6") +
data(population_spikes) + bar + x(col.lon) + y(col.lat) + z(col.reach) + color(col.continent) +
globe(turn = 20, tilt = 12) +
title("World population, standing on its places"))data(world_borders) + path + x(:lon) + y(:lat) + group(:piece) +
style(color = "#c9ccd6") + data(population_spikes) + bar + x(:lon) +
y(:lat) + z(:reach) + color(:continent) + globe(turn = 20, tilt = 12) +
title("World population, standing on its places")plot(data(world_borders), path, x(col.lon), y(col.lat), group(col.piece),
style({ color: "#c9ccd6" }), data(population_spikes), bar, x(col.lon),
y(col.lat), z(col.reach), color(col.continent),
globe({ turn: 20, tilt: 12 }),
title("World population, standing on its places"))“Given the world borders: paths, x is lon, y is lat, grouped by piece; then given the spikes: bars, x is lon, y is lat, z is reach, color by continent, on the globe.”
Finding a country’s center and joining the population are the host language’s job, as the join was on the map. Shaping the measure is its job too. The square root keeps China’s spike from making the rest look tiny, and a scale parameter would only be a second way of writing that one line.
Look at the edge of the disk. The sphere itself clips a spike, so one standing just behind that edge still shows its top when it is tall enough. The tallest spikes there are seen from the side. A spike facing you foreshortens toward a point, which is why a plot like this is read by turning it.
36.8 The depth goes to the cube
Each earthquake here carries a third number: elevation, its depth below the surface. That is not a length to raise from the surface but a third place coordinate, and on the globe z belongs to the spike alone. So gog refuses the sentence below rather than guessing at it. The refusal names three ways forward: add a bar layer, drop z(), or draw the same table in the cube.
data(quakes_fiji) + point + x(east) + y(north) + z(elevation) +
globe(turn = 178, tilt = -18)(data(quakes_fiji) + point + x(col.east) + y(col.north) + z(col.elevation) +
globe(turn = 178, tilt = -18))data(quakes_fiji) + point + x(:east) + y(:north) + z(:elevation) +
globe(turn = 178, tilt = -18)plot(data(quakes_fiji), point, x(col.east), y(col.north),
z(col.elevation), globe({ turn: 178, tilt: -18 }))Error:
! gog: `z(...)` on a globe is the radius, and only a `bar` reads it — a spike standing at its place, measuring outward from the surface. Add a `bar` layer to raise spikes, drop `z(...)` to keep the surface marks, or drop `globe()` for the cube, where every position mark reads `z`.
gog: nothing was rendered. Fix the above, or set GOG_STRICT=0 to draw anyway.
A place with a depth belongs in the cube, the space with a third position. space() names the view with the words globe() uses:
data(quakes_fiji) + point + x(east) + y(north) + z(elevation) +
color(slab) + space(turn = 130, tilt = 25) +
title("The same earthquakes, read by depth")(data(quakes_fiji) + point + x(col.east) + y(col.north) + z(col.elevation) +
color(col.slab) + space(turn = 130, tilt = 25) +
title("The same earthquakes, read by depth"))data(quakes_fiji) + point + x(:east) + y(:north) + z(:elevation) +
color(:slab) + space(turn = 130, tilt = 25) +
title("The same earthquakes, read by depth")plot(data(quakes_fiji), point, x(col.east), y(col.north),
z(col.elevation), color(col.slab), space({ turn: 130, tilt: 25 }),
title("The same earthquakes, read by depth"))“Given quakes Fiji: points, x is east, y is north, z is elevation, color by slab.”
Drag this one too: the globe turns the earth, and the cube turns the cloud, one gesture for both. slab names the depth band each quake falls in. The earlier plots looked straight down on these quakes; this one shows the sinking sheet of ocean floor they sit on.
36.9 What a globe refuses
You may try a line through the earthquakes, as you would on a flat page. Both positions carry the place, exactly as on the map, so a mark that measures along an axis has no axis left:
data(quakes_fiji) + line + x(east) + y(north) + globe()data(quakes_fiji) + line + x(col.east) + y(col.north) + globe()data(quakes_fiji) + line + x(:east) + y(:north) + globe()plot(data(quakes_fiji), line, x(col.east), y(col.north), globe())Error:
! gog: `line` measures along an axis, and a `globe()` plot has none to spare — longitude and latitude use both. Drop `globe()` to draw `line` flat, or use `point`, `bar`, `text`, `path`, `rule`, or `zone`. To carry a quantity on the globe, put it on a channel instead of an axis: `point + size(<column>)` sizes each place by it, and `color(<column>)` shades it.
gog: nothing was rendered. Fix the above, or set GOG_STRICT=0 to draw anyway.
The quantity moves to a channel instead, size() or color(), or it stands on the radius as a spike, the way the population spikes above do.
A latitude past a pole names no place, so a view cannot face that latitude:
data(quakes_fiji) + point + x(east) + y(north) + globe(tilt = 100)data(quakes_fiji) + point + x(col.east) + y(col.north) + globe(tilt = 100)data(quakes_fiji) + point + x(:east) + y(:north) + globe(tilt = 100)plot(data(quakes_fiji), point, x(col.east), y(col.north),
globe({ tilt: 100 }))Error:
! gog: `globe(tilt = 100)` is outside -90 to 90, which is where a latitude lives. At 90 the view faces straight down on the north pole and at -90 on the south; there is no place past either. Use `globe(tilt = 90)` for the nearest view, or `globe(turn = )` to swing around the earth instead — a bearing wraps and a latitude does not.
gog: nothing was rendered. Fix the above, or set GOG_STRICT=0 to draw anyway.
And a globe draws no axes at all, so naming one is refused rather than accepted and dropped:
data(quakes_fiji) + point + x(east) + y(north) + globe() + x_label("Longitude")data(quakes_fiji) + point + x(col.east) + y(col.north) + globe() + x_label("Longitude")data(quakes_fiji) + point + x(:east) + y(:north) + globe() +
x_label("Longitude")plot(data(quakes_fiji), point, x(col.east), y(col.north), globe(),
x_label("Longitude"))Error:
! gog: an axis label names an axis, and a `globe()` plot draws none — a sphere has no edge to write one on, and the graticule is the reference instead. Drop it; `title()` still names the plot.
gog: nothing was rendered. Fix the above, or set GOG_STRICT=0 to draw anyway.
The map and the globe are one meaning with two pictures of it. The map trades the sphere for a page a reader sees whole. The globe keeps the sphere and trades away the far half. Moving between them costs one word.