Skip to content

Commit

Permalink
Fix merge conflict
Browse files Browse the repository at this point in the history
  • Loading branch information
Robinlovelace committed Oct 5, 2024
2 parents 8e80fd0 + 5d6f3b1 commit cb7bcda
Show file tree
Hide file tree
Showing 11 changed files with 1,625 additions and 25 deletions.
6 changes: 4 additions & 2 deletions 05-raster-vector.qmd
Original file line number Diff line number Diff line change
Expand Up @@ -696,7 +696,8 @@ california_raster1 = rasterio.features.rasterize(
out_shape=shape,
transform=transform,
all_touched=True,
fill=np.nan
fill=np.nan,
dtype=np.float64
)
```

Expand All @@ -707,7 +708,8 @@ california_raster2 = rasterio.features.rasterize(
[(g, 1) for g in california.geometry],
out_shape=shape,
transform=transform,
fill=np.nan
fill=np.nan,
dtype=np.float64
)
```

Expand Down
42 changes: 21 additions & 21 deletions 08-mapping.qmd
Original file line number Diff line number Diff line change
Expand Up @@ -76,22 +76,22 @@ Maps have been used for several thousand years for a wide variety of purposes.
Historic examples include maps of buildings and land ownership in the Old Babylonian dynasty more than 3000 years ago and Ptolemy's world map in his masterpiece Geography nearly 2000 years ago [@talbert_ancient_2014].

Map making has historically been an activity undertaken only by, or on behalf of, the elite.
This has changed with the emergence of open source mapping software such as mapping packages in Python, R, and other languages, and the "print composer" in QGIS, which enable anyone to make high-quality maps, enabling "citizen science".
This has changed with the emergence of open-source mapping software such as mapping packages in Python, R, and other languages, and the 'print composer' in QGIS, which enable anyone to make high-quality maps, enabling 'citizen science'.
Maps are also often the best way to present the findings of geocomputational research in a way that is accessible.
Map making is therefore a critical part of geocomputation and its emphasis not only on describing, but also changing the world.

Basic static display of vector layers in Python is done with the `.plot` method or the `rasterio.plot.show` function, for vector layers and rasters, as we saw in Sections @sec-vector-layers and @sec-using-rasterio, respectively.
Other, more advanced uses of these methods, were also encountered in subsequent chapters, when demonstrating the various outputs we got.
In this chapter, we provide a comprehensive summary of the most useful workflows of these two methods for creating static maps (@sec-static-maps).
Static maps can be easily shared and viewed (whether digitally or in print), however they can only convey as much information as a static image can.
Interactive maps provide much more flexibilty in terms of user experience and amount of information, however they often require more work to design and effectively share.
Interactive maps provide much more flexibility in terms of user experience and amount of information, however they often require more work to design and effectively share.
Thus, in @sec-interactive-maps, we move on to elaborate on the `.explore` method for creating interactive maps, which was also briefly introduced earlier in @sec-vector-layers.

## Static maps {#sec-static-maps}

Static maps are the most common type of visual output from geocomputation.
For example, we have been using `.plot` and `rasterio.plot.show` throughout the book, to display **geopandas** and **rasterio** geocomputation results, respectively.
In this section we systematically review and elaborate on the various properties that can be customized when using those functions.
In this section, we systematically review and elaborate on the various properties that can be customized when using those functions.

A static map is basically a digital image.
When stored in a file, standard formats include `.png` and `.pdf` for graphical raster and vector outputs, respectively.
Expand Down Expand Up @@ -180,11 +180,11 @@ nz.plot(column='Median_income', legend=True);
```

The default color scale which you see in @fig-plot-symbology is `cmap='viridis'`.
The `cmap` ("color map") argument can be used to specify one of countless color scales.
The `cmap` ('color map') argument can be used to specify one of countless color scales.
A first safe choice is often the ColorBrewer[^colorbrewer] collection of color scales, specifically designed for mapping.
Any color scale can be reversed, using the `_r` suffix.
Finally, other color scales are available: see the **matplotlib** colormaps article[^matplotlib_colormaps] for details.
The following code sections demonstrates three color scale specifications other than the default (@fig-plot-symbology-colors).
The following code section demonstrates three-color scale specifications other than the default (@fig-plot-symbology-colors).

[^colorbrewer]: <https://colorbrewer2.org/>
[^matplotlib_colormaps]: <https://matplotlib.org/stable/tutorials/colors/colormaps.html>
Expand Down Expand Up @@ -252,7 +252,7 @@ fig.colorbar(i, ax=ax);

Labels are often useful to annotate maps and identify the location of specific features.
GIS software, as opposed to **matplotlib**, has specialized algorithms for label placement, e.g., to avoid overlaps between adjacent labels.
Furthermore, editing in graphical editing software is sometimes used for fine tuning of label placement.
Furthermore, editing in graphical editing software is sometimes used for fine-tuning of label placement.
Nevertheless, simple labels added within the Python environment can be a good starting point, both for interactive exploration and sharing analysis results.

To demonstrate it, suppose that we have a layer `nz1` of regions comprising the New Zealand southern Island.
Expand Down Expand Up @@ -313,9 +313,9 @@ ctr.apply(
);
```

It should be noted that sometimes we wish to add text labels "manually", one by one, rather than use a loop or `.apply`.
It should be noted that sometimes we wish to add text labels 'manually', one by one, rather than use a loop or `.apply`.
For example, we may want to add labels of specific locations not stored in a layer, or to have control over the specific properties of each label.
To add text labels manually, we can run the `.annotate` expressions one at a time, as shown in the code section below recreating the last result with the "manual" approach (@fig-labels-points2).
To add text labels manually, we can run the `.annotate` expressions one at a time, as shown in the code section below recreating the last result with the 'manual' approach (@fig-labels-points2).

```{python}
#| label: fig-labels-points2
Expand Down Expand Up @@ -406,12 +406,12 @@ layer2.plot(ax=base, zorder=2);

### Basemaps

Basemaps, or background layers, are often useful to provide context to the displayed layers (which are in the "foreground").
Basemaps, or background layers, are often useful to provide context to the displayed layers (which are in the 'foreground').
Basemaps are ubiquitous in interactive maps (see @sec-interactive-maps).
However, they are often useful in static maps too.

Basemaps can be added to **geopandas** static plots using the **contextily** package.
A preliminary step is to convert our layers to `EPSG:3857` ("Web Mercator"), to be in agreement with the basemaps, which are typically provided in this CRS[^reproject_tiles].
A preliminary step is to convert our layers to `EPSG:3857` ('Web Mercator'), to be in agreement with the basemaps, which are typically provided in this CRS[^reproject_tiles].
For example, let's take the small `"Nelson"` polygon from `nz`, and reproject it to `3857`.

[^reproject_tiles]: Another option is to reproject the tiles to match the CRS of the foreground layers; this is less commonly used workflow, as it may lead to distorted appearance of the background layer.
Expand All @@ -421,7 +421,7 @@ nzw = nz[nz['Name'] == 'Nelson'].to_crs(epsg=3857)
```

To add a basemap, we use the `contextily.add_basemap` function, similarly to the way we added multiple layers (@sec-plot-static-layers).
The default basemap is "OpenStreetMap".
The default basemap is 'OpenStreetMap'.
You can specify a different basemap using the `source` parameter, with one of the values in `cx.providers` (@fig-basemap).

```{python}
Expand Down Expand Up @@ -471,9 +471,9 @@ for i in range(len(vars)):
ax[i].set_title(vars[i])
```

In case we prefer a specific layout, rather than one row or one column, we can initialize the required number or rows and columns, as in `plt.subplots(nrows,ncols)`, "flatten" `ax`, so that the facets are still accessible using a single index `ax[i]` (rather than the default `ax[i][j]`), and plot into `ax[i]`.
In case we prefer a specific layout, rather than one row or one column, we can initialize the required number or rows and columns, as in `plt.subplots(nrows,ncols)`, 'flatten' `ax`, so that the facets are still accessible using a single index `ax[i]` (rather than the default `ax[i][j]`), and plot into `ax[i]`.
For example, here is how we can reproduce the last plot, this time in a $2 \times 2$ layout, instead of a $1 \times 4$ layout (@fig-faceted-map2).
One more modification we are doing here is hiding the axis ticks and labels, to make the map less "crowded", using `ax[i].xaxis.set_visible(False)` (and same for `.yaxis`).
One more modification we are doing here is hiding the axis ticks and labels, to make the map less 'crowded', using `ax[i].xaxis.set_visible(False)` (and same for `.yaxis`).

```{python}
#| label: fig-faceted-map2
Expand All @@ -487,13 +487,13 @@ for i in range(len(vars)):
ax[i].yaxis.set_visible(False)
```

It is also possible to "manually" specify the properties of each panel, and which row/column it goes in (e.g., @fig-spatial-aggregation-different-functions).
It is also possible to 'manually' specify the properties of each panel, and which row/column it goes in (e.g., @fig-spatial-aggregation-different-functions).
This can be useful when the various panels have different components, or even completely different types of plots (e.g., @fig-zion-transect), making automation with a `for` loop less applicable.
For example, here is a plot similar to @fig-faceted-map2, but specifying each panel using a separate expression instead of using a `for` loop (@fig-faceted-map3).

```{python}
#| label: fig-faceted-map3
#| fig-cap: Two-dimensional layout in a faceted map, using "manual" specification of the panels
#| fig-cap: 2D layout in a faceted map, using "manual" specification of the panels
fig, ax = plt.subplots(ncols=2, nrows=int(len(vars)/2), figsize=(6, 6))
nz.plot(ax=ax[0][0], column=vars[0], legend=True)
ax[0][0].set_title(vars[0])
Expand Down Expand Up @@ -546,12 +546,12 @@ plt.savefig('output/plot_rasterio2.svg', dpi=300)
While static maps can enliven geographic datasets, interactive maps can take them to a new level.
Interactivity can take many forms, the most common and useful of which is the ability to pan around and zoom into any part of a geographic dataset overlaid on a 'web map' to show context.
Less advanced interactivity levels include popups which appear when you click on different features, a kind of interactive label.
More advanced levels of interactivity include the ability to tilt and rotate maps, and the provision of "dynamically linked" sub-plots which automatically update when the user pans and zooms [@pezanowski_senseplace3_2018].
More advanced levels of interactivity include the ability to tilt and rotate maps, and the provision of 'dynamically linked' sub-plots which automatically update when the user pans and zooms [@pezanowski_senseplace3_2018].

The most important type of interactivity, however, is the display of geographic data on interactive or 'slippy' web maps.
Significant features of web maps are that (1) they eventually comprise static HTML files, easily shared and accessed by a wide audience, and (2) they can "grab" content (e.g., basemaps) or use services from other locations on the internet, that way providing detailed context without much requiring much effort from the person who created the map.
Significant features of web maps are that (1) they eventually comprise static HTML files, easily shared and accessed by a wide audience, and (2) they can 'grab' content (e.g., basemaps) or use services from other locations on the internet, that way providing detailed context without much requiring much effort from the person who created the map.
The most popular approaches for web mapping, in Python and elsewhere, are based on the Leaflet JavaScript library [@dorman2020introduction].
The **folium** Python package provides an extensive interface to create customized web maps based on Leaflet; it is recommended for highly-custimized maps.
The **folium** Python package provides an extensive interface to create customized web maps based on Leaflet; it is recommended for highly customized maps.
However, the **geopandas** wrapper `.explore`, introduced in @sec-vector-layers, can be used for a wide range of scenarios which are often sufficient.
This is what we cover in this section.

Expand Down Expand Up @@ -629,7 +629,7 @@ Accordingly, for points, we can set the `marker_type`, to one of:
- `'circle'`---A vector circle with radius specified in $m$
- `'circle_marker'`---A vector circle with radius specified in pixels (the default)

For example, the following expression draws `'circe_marker`' points with 20 pixel radius, green fill, and black outline (@fig-explore-styling-points).
For example, the following expression draws `'circe_marker`' points with 20-pixel radius, green fill, and black outline (@fig-explore-styling-points).

::: {.content-visible when-format="html"}
```{python}
Expand Down Expand Up @@ -665,7 +665,7 @@ map_to_png.map_to_png(nz_height.explore(
:::

@fig-explore-styling-points2 demonstrates the `'marker_type'` option.
Note that the above-mentioned styling properties (other then `opacity`) are not applicable when using `marker_type='marker'`, because the markers are fixed PNG images.
Note that the above-mentioned styling properties (other than `opacity`) are not applicable when using `marker_type='marker'`, because the markers are fixed PNG images.

::: {.content-visible when-format="html"}
```{python}
Expand Down Expand Up @@ -716,7 +716,7 @@ map_to_png.map_to_png(nz_height.explore(m=m, color='red'), 'fig-explore-layers')
![Displaying multiple layers in an interactive map with `.explore`](images/fig-explore-layers.png){#fig-explore-layers}
:::

One of the advantages of interactive maps is the ability to turn layers "on" and "off".
One of the advantages of interactive maps is the ability to turn layers 'on' and 'off'.
This capability is implemented in `folium.LayerControl` from package **folium**, which the **geopandas** `.explore` method is a wrapper of.
For example, this is how we can add a layer control for the `nz` and `nz_height` layers (@fig-explore-layers-controls).
Note the `name` properties, used to specify layer names in the control, and the `collapsed` property, used to specify whether the control is fully visible at all times (`False`), or only on mouse hover (`True`, the default).
Expand Down
Loading

0 comments on commit cb7bcda

Please sign in to comment.