Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Chapter 5: Raster-Vector Interaction #7

Merged
merged 50 commits into from
Sep 20, 2024
Merged
Show file tree
Hide file tree
Changes from 42 commits
Commits
Show all changes
50 commits
Select commit Hold shift + click to select a range
b18229e
Add all data from geocompy
asinghvi17 Sep 19, 2024
de767ac
Add more geo packages to Project.toml
asinghvi17 Sep 19, 2024
87146a1
Add StatsBase
asinghvi17 Sep 19, 2024
e6707dc
WIP: first half of raster-vector interaction
asinghvi17 Sep 19, 2024
42bc191
Fix typos
asinghvi17 Sep 19, 2024
b24fd62
WIP rasterization
asinghvi17 Sep 19, 2024
fb49b9a
Add `output` to `.gitignore`
asinghvi17 Sep 19, 2024
6a0059e
Merge remote-tracking branch 'origin/main' into as/05-raster-vector
asinghvi17 Sep 19, 2024
f93af2f
Implement "Raster from scratch" in chapter 1 to provide data
asinghvi17 Sep 19, 2024
dfdb598
Activate more plotting
asinghvi17 Sep 19, 2024
ecafce4
Fix axes in ch1
asinghvi17 Sep 19, 2024
decc049
Add raster to polygons to ch5
asinghvi17 Sep 19, 2024
4be8622
Add GeoJSON to Project.toml
asinghvi17 Sep 19, 2024
ddb7021
Add raster to points section
asinghvi17 Sep 19, 2024
96c0653
reached the end!!
asinghvi17 Sep 19, 2024
a355efe
Set execute-dir to "project" so that we don't have to modify paths
asinghvi17 Sep 19, 2024
5da5423
Import GeoFormatTypes initially
asinghvi17 Sep 19, 2024
a09a8bb
Fix typo like thihgs
asinghvi17 Sep 19, 2024
1820138
Force all writes
asinghvi17 Sep 19, 2024
7771d7c
Embolden Rasters.jl
asinghvi17 Sep 19, 2024
83d15f4
Fix execution directory
asinghvi17 Sep 19, 2024
a451087
Merge remote-tracking branch 'origin/main' into as/05-raster-vector
asinghvi17 Sep 19, 2024
f952ae3
Set GeometryOps.jl compat in directory
asinghvi17 Sep 19, 2024
5fde60c
Display all figures so that they get picked up
asinghvi17 Sep 19, 2024
7306c70
Set the execution dir
asinghvi17 Sep 19, 2024
5f5f73c
python->julia cells in 01
asinghvi17 Sep 19, 2024
dacc033
Set the theme such that heatmaps and surfaces are rasterized
asinghvi17 Sep 19, 2024
6c275a1
Add custom packages in CI
asinghvi17 Sep 19, 2024
1bfddc5
Publish on pull request (dangerous, revert soon!)
asinghvi17 Sep 19, 2024
4c3bc07
Construct a DataFrame from properties and attributes
asinghvi17 Sep 19, 2024
672e749
more description
asinghvi17 Sep 19, 2024
7d99dfe
Fix shell
asinghvi17 Sep 19, 2024
9b63ea2
maybe encase in quotes
asinghvi17 Sep 19, 2024
603a83c
`{0}` is actually a file
asinghvi17 Sep 19, 2024
3d3694e
Set the number of dataframe rows in the environment
asinghvi17 Sep 19, 2024
d2ff1be
Add execute-dir as frontmatter to all files
asinghvi17 Sep 19, 2024
5b88ab4
Simplify the transect distance calculation
asinghvi17 Sep 19, 2024
d6fb4e8
fig |> display -> display(fig)
asinghvi17 Sep 19, 2024
7bced90
Check strict equality with missingval
asinghvi17 Sep 19, 2024
6aaed34
Activate quarto as a shared env in setup
asinghvi17 Sep 19, 2024
e9413e1
Create the output directory explicitly
asinghvi17 Sep 19, 2024
fc63dab
Add Makie compatible DD + Rasters
asinghvi17 Sep 19, 2024
f6c1fdd
add Manifest to gitignore
asinghvi17 Sep 20, 2024
1835ce1
Add comments to the custom branches about why we need them
asinghvi17 Sep 20, 2024
b2f063a
Merge branch 'as/05-raster-vector' of https://github.com/geocompx/geo…
asinghvi17 Sep 20, 2024
00de157
Apply suggestions to chapter 1
asinghvi17 Sep 20, 2024
c5cedf4
Fix typo in ch1
asinghvi17 Sep 20, 2024
7e63a99
Apply suggestions for chapter 5
asinghvi17 Sep 20, 2024
3153d6f
Add a tabset to show how you can use geometries in
asinghvi17 Sep 20, 2024
c0ecb22
Apply suggestions from code review
asinghvi17 Sep 20, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions .github/workflows/main.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ on:
push:
branches:
[main]
pull_request:
name: Quarto Publish
jobs:
bookdown:
Expand All @@ -14,14 +15,30 @@ jobs:
- uses: actions/checkout@v3
- uses: julia-actions/setup-julia@v2
- uses: julia-actions/cache@v2
- name: Set up custom Julia dependencies
run: |
using Pkg
Pkg.activate("quarto"; shared = true)
Pkg.add(url = "https://github.com/asinghvi17/QuartoNotebookRunner.jl", rev = "as/execute-dir")
asinghvi17 marked this conversation as resolved.
Show resolved Hide resolved
Pkg.instantiate()
Pkg.activate(".")
Pkg.add([
PackageSpec(url = "https://github.com/asinghvi17/Rasters.jl", rev = "as/makie_and_cf"),
PackageSpec(url = "https://github.com/rafaqz/DimensionalData.jl", rev = "main"),
asinghvi17 marked this conversation as resolved.
Show resolved Hide resolved
])
shell: julia {0}
- uses: julia-actions/julia-buildpkg@v1

- name: Set up Quarto
uses: quarto-dev/quarto-actions/setup@v2
env:
QUARTO_JULIA_PROJECT: "@quarto"

- name: Render and Publish
uses: quarto-dev/quarto-actions/publish@v2
with:
target: gh-pages
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
QUARTO_JULIA_PROJECT: "@quarto"
DATAFRAMES_ROWS: "6"
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,4 @@ libs/
/.quarto/
docs
/_site/
output/*
13 changes: 13 additions & 0 deletions Project.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,19 @@
[deps]
ArchGDAL = "c9ce4bd3-c3d5-55b8-8973-c0e20141b8c3"
CairoMakie = "13f3f980-e62b-5c42-98c6-ff1f3baf88f0"
DataFrames = "a93c6f00-e57d-5684-b7b6-d8193f3e46c0"
DimensionalData = "0703355e-b756-11e9-17c0-8b28908087d0"
GeoDataFrames = "62cb38b5-d8d2-4862-a48e-6a340996859f"
GeoFormatTypes = "68eda718-8dee-11e9-39e7-89f7f65f511f"
GeoInterface = "cf35fbd7-0cd7-5166-be24-54bfbe79505f"
GeoJSON = "61d90e0f-e114-555e-ac52-39dfb47a3ef9"
GeoMakie = "db073c08-6b98-4ee5-b6a4-5efafb3259c6"
GeometryOps = "3251bfac-6a57-4b6d-aa61-ac1fef2975ab"
LibGEOS = "a90b1aa1-3769-5649-ba7e-abc5a9d163eb"
Proj = "c94c279d-25a6-4763-9509-64d165bea63e"
Rasters = "a3a2b9e3-a471-40c9-b274-f788e487c689"
Statistics = "10745b16-79ce-11e8-11f9-7d13ad32a3b2"
StatsBase = "2913bbd2-ae8a-5f71-8c99-4fb6c76f3a91"

[compat]
GeometryOps = "0.1.12"
1 change: 1 addition & 0 deletions _quarto.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
project:
type: book
output-dir: docs
execute-dir: project

book:
title: "Geocomputation with Julia"
Expand Down
125 changes: 124 additions & 1 deletion chapters/01-spatial-data.qmd
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
---
engine: julia
project:
execute-dir: project
---

# Geographic data in Julia {#sec-spatial-class}
Expand All @@ -9,10 +11,16 @@ using Pkg
Pkg.status()
```

```{julia}
#| echo: false
mkpath("output")
```


## Introduction
```{julia}
using GeoDataFrames
df = GeoDataFrames.read("../data/world.gpkg")
df = GeoDataFrames.read("data/world.gpkg")
asinghvi17 marked this conversation as resolved.
Show resolved Hide resolved
```

```{julia}
Expand All @@ -21,3 +29,118 @@ using GeoMakie

f, a, p = poly(df.geom)
```


### Raster from scratch {#sec-raster-from-scratch}

In this section, we are going to demonstrate the creation of rasters from scratch.
We will construct two small rasters, `elev` and `grain`, which we will use in examples later in the book.
Unlike creating a vector layer (see @sec-vector-layer-from-scratch), creating a raster from scratch is rarely needed in practice because aligning a raster with the proper spatial extent is challenging to do programmatically ("georeferencing" tools in GIS software are a better fit for the job).
Nevertheless, the examples will be helpful to become more familiar with the **Rasters.jl** data structures.

```{julia}
using Rasters
import GeoFormatTypes as GFT
```

Conceptually, a raster is an array combined with georeferencing information, whereas the latter comprises:

- A transformation matrix, containing the origin and resolution, thus linking pixel indices with coordinates in a particular coordinate system
asinghvi17 marked this conversation as resolved.
Show resolved Hide resolved
- A CRS definition, specifying the association of that coordinate system with the surface of the earth (optional)
asinghvi17 marked this conversation as resolved.
Show resolved Hide resolved

Therefore, to create a raster, we first need to have an array with the values, and then supplement it with the georeferencing information.
Let's create the arrays `elev` and `grain`.
The `elev` array is a $6 \times 6$ array with sequential values from `1` to `36`.
It can be created as follows using base Julia functions.

```{julia}
elev = reshape(UInt8(1):UInt8(36), (6, 6))
```

The `grain` array represents a categorical raster with values `0`, `1`, `2`, corresponding to categories "clay", "silt", "sand", respectively.
We will create it from a specific arrangement of pixel values, using `reshape`.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does Rasters support CategoricalArrays? Might be nice to introduce some Julia concepts of combining everything seamlessly?

Copy link
Collaborator Author

@asinghvi17 asinghvi17 Sep 20, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It does but I don't know if that will write to file correctly, will try it!


```{julia}
v = UInt8[
1, 0, 1, 2, 2, 2,
0, 2, 0, 0, 2, 1,
0, 2, 2, 0, 0, 2,
0, 0, 1, 1, 1, 1,
1, 1, 1, 2, 1, 1,
2, 1, 2, 2, 0, 2
]
grain = reshape(v, (6, 6))
```

Note that in both cases, we are using the `uint8` (unsigned integer in 8 bits, i.e., `0-255`) data type, which is sufficient to represent all possible values of the given rasters (see @tbl-numpy-data-types).
This is the recommended approach for a minimal memory footprint.

What is missing now is the georeferencing information (see @sec-using-rasterio).
asinghvi17 marked this conversation as resolved.
Show resolved Hide resolved
In this case, since the rasters are arbitrary, we also set up arbitrary dimension lookups for the `x` and `y` axes, where:

- The origin ($x_{min}$, $y_{max}$) is at `-1.5,1.5`
- The raster resolution ($delta_{x}$, $delta_{y}$) is `0.5,-0.5`

We can add this information using `rasterio.transform.from_origin`, and specifying `west`, `north`, `xsize`, and `ysize` parameters.
The resulting transformation matrix object is hereby named `new_transform`.

```{julia}
new_x = X(range(-1.5, step=0.5, length=6))
new_y = Y(range(1.0, step=-0.5, length=6))
```

We can now construct a `Raster` object, from the `elev` array and the dimensions `new_x` and `new_y`:

```{julia}
elev_raster = Raster(elev, (new_x, new_y))
```

The raster can now be plotted in its coordinate system, passing the array `elev` along with the transformation matrix `new_transform` to `rasterio.plot.show` (@fig-rasterio-plot-elev).
The raster can now be plotted in its coordinate system, passing the array `elev` along with the transformation matrix `new_transform` to `rasterio.plot.show` (@fig-rasterio-plot-elev).
asinghvi17 marked this conversation as resolved.
Show resolved Hide resolved

```{julia}
#| label: fig-rasterio-plot-elev
#| fig-cap: Plot of the `elev` raster, a minimal example of a continuous raster, created from scratch
plot(elev_raster)
```

The `grain` raster can be plotted the same way, as we are going to use the same transformation matrix for it as well (@fig-rasterio-plot-grain).

```{julia}
#| label: fig-rasterio-plot-grain
#| fig-cap: Plot of the `grain` raster, a minimal example of a categorical raster, created from scratch
plot(Raster(grain, (new_x, new_y)))
```

At this point, we have two rasters, each composed of an array and related dimension lookups.
We can work with the raster using **Rasters.jl** by:

- Keeping in mind that any other layer we use in the analysis is in the same CRS

Finally, to export the raster for permanent storage, along with the spatial metadata, we need to go through the following steps:

1. Create a raster file (where we set the lookups and the CRS, among other settings)
2. Write the array with raster values into the connection
3. Close the connection

Don't worry if the code below is unclear; the concepts related to writing raster data to file will be explained in @sec-data-output-raster.
For now, for completeness, and also to use these rasters in subsequent chapters without having to re-create them from scratch, we just provide the code for exporting the `elev` and `grain` rasters into the `output` directory.
In the case of `elev`, we do it as follows with the `Rasters.write` functions and methods of the **Rasters.jl** package.

```{julia}
write("output/elev.tif", Rasters.setcrs(elev_raster, GFT.EPSG(4326)); force = true)
asinghvi17 marked this conversation as resolved.
Show resolved Hide resolved
```

Note that the CRS we (arbitrarily) set for the `elev` raster is WGS84, defined using `crs=4326` according to the EPSG code.

Exporting the `grain` raster is done in the same way, with the only differences being the file name and the array we write into the connection.

```{julia}
write("output/grain.tif", Raster(grain, (new_x, new_y); crs = GFT.EPSG(4326)); force = true)
```

As a result, the files `elev.tif` and `grain.tif` are written into the `output` directory.
We are going to use these small raster files later on in the examples (for example, @sec-raster-subsetting).

Note that the transform matrices and dimensions of `elev` and `grain` are identical.
This means that the rasters are overlapping, and can be combined into one two-band raster, processed in raster algebra operations (@sec-map-algebra), etc.
2 changes: 2 additions & 0 deletions chapters/02-attribute-operations.qmd
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
---
engine: julia
project:
execute-dir: project
---

# Attribute data operations {#sec-attr}
Expand Down
2 changes: 2 additions & 0 deletions chapters/03-spatial-operations.qmd
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
---
engine: julia
project:
execute-dir: project
---

# Spatial data operations {#sec-spatial-operations}
Expand Down
2 changes: 2 additions & 0 deletions chapters/04-geometry-operations.qmd
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
---
engine: julia
project:
execute-dir: project
---

# Geometry operations {#sec-geometric-operations}
Expand Down
Loading
Loading