Example conversions
The following examples demonstrate how to convert the R code at https://github.com/jjennewein/MDA_2023-24 into equivalent Python code.
0.field buffer.R | 0.field buffer.py |
---|---|
library(sf) | import geopandas as gpd |
Loads a library for handling geospatial data, including working with shapefiles. In the Python code, gpd is an alias for the geopandas library Using the alias simply reduces typing and improves code readability. | |
setwd(dirname(rstudioapi::getActiveDocumentContext()$path)) | |
RStudio defaults to a global working directory, which means file paths must be specified explicitly. Python defaults to the folder where the source code is located. Jupyter Notebook defaults to where you launch the notebook server.
The original file path was To avoid referencing This way, the R program can run without requiring the full path in filenames. | |
mda = st_read("MDAEnrollment2023-2024.shp") %>% st_transform(., crs = 32618) %>% st_buffer(., dist = -30) %>% dplyr::filter(!st_is_empty(.)) | mda = gpd.read_file("MDAEnrollment2023-2024.shp") \ .to_crs(epsg=32618) mda["geometry"] = mda.geometry.buffer(-30) mda = mda[~mda.geometry.is_empty] |
This code:
R uses In Python, a backslash ( Also, note that the dots in | |
st_write(mda, "MDAEnrollment2023-2024_Buff30m.shp", append=FALSE) | mda.to_file("MDAEnrollment2023-2024_Buff30m.shp") |
Writes the buffered data frame to a new shapefile. I added |
1.Extract_HLS_L30 .R | 1.Extract_HLS_L30 .py |
---|---|
library(dplyr) library(terra) library(sf) library(exactextractr) library(glue) | import pandas as pd import rasterio import geopandas as gpd import os import glob import numpy as np from rasterstats import zonal_stats |
Syntax is easy. Knowing which libraries to use – not so much! Here’s a quick description of each Python library used for this program:
| |
process_shapefile <- function(sensor, NIR_band) { | def process_shapefile(sensor, NIR_band): |
Defines a function that takes the | |
setwd(base_dir) tiles <- list.files() | os.chdir(base_dir) tiles = os.listdir() |
Changes to the base directory and get a list of the tiles. | |
extract = fields %>% #set up the output data frame extractions st_drop_geometry() %>% dplyr::select() | extract = fields.iloc[:, 0:0] |
Creates a dataframe with the number of rows in fields but with no columns. Note the simple syntax in Python. The single colon | |
for(i in 1:length(tiles)) { | for i, tile in enumerate(tiles): |
Creates a loop to iterate through the tiles. Note that the Python code could be written as | |
tile_dir <- file.path(base_dir, tiles[i]) setwd(tile_dir) | tile_dir = os.path.join(base_dir, tile) os.chdir(tile_dir) |
Changes to the directory of the current tile. | |
scenes <- list.files(pattern = glob2rx(glue("HLS.{sensor}*"))) | scenes = glob.glob(f"HLS.{sensor}*") |
Grabs the files that match the pattern Python’s f strings work similarly to R’s glue function. | |
for (j in 1:length(scenes)) { | for j, scene in enumerate(scenes): |
Creates a loop to iterate through each tile's scenes. | |
print(glue("Processing scene {j} of {length(scenes)} in tile {i} of {length(tiles)}")) | print(f"Processing scene {j + 1} of {len(scenes)} in tile {i + 1} of {len(tiles)}") |
Shows the current status. | |
scene_dir <- file.path(tile_dir, scenes[j]) setwd(scene_dir) | scene_dir = os.path.join(tile_dir, scene) os.chdir(scene_dir) |
Changes to the scene/date folder. | |
bands = list.files() #list rasters in the scene/date folder list_NIR <- bands[substring(bands, 36, 38) == NIR_band] list_B04 <- bands[substring(bands, 36, 38) == "B04"] | list_NIR = glob.glob(f"*.{NIR_band}.tif")[0] list_B04 = glob.glob("*.B04.tif")[0] |
Grabs the filenames of the NIR and B04 bands. Note how Python’s | |
rast.stk <- rast(c(list_B04, list_NIR)) | rast_stk = [rasterio.open(list_B04), rasterio.open(list_NIR)] |
Loads the raster stack of the B04 and NIR bands. | |
names(rast.stk) <- c(substring(list_B04, 1, 38), substring(list_NIR, 1, 38)) | rast_stk_named = {name: rast for name, rast in zip(band_names, rast_stk)} |
Sets the names of the raster stack based on file names. | |
list_cloud_mask <- which(substring(bands, 36, 38) == "Fma") cloud_mask <- rast(bands[list_cloud_mask]) | list_cloud_mask = glob.glob("*.Fmask.tif")[0] with rasterio.open(list_cloud_mask) as cloud_mask_src: cloud_mask = cloud_mask_src.read(1).astype(float) |
cloud_mask0 <- cloud_mask cloud_mask64 <- cloud_mask cloud_mask128 <- cloud_mask | cloud_mask0 = np.copy(cloud_mask) cloud_mask64 = np.copy(cloud_mask) cloud_mask128 = np.copy(cloud_mask) |
The R code creates references to This is an optimization technique, which prevents R from copying data that might not change. However, the data does change in the following code. When that happens, R will automatically change the references to copies of The Python code makes this explicit by creating copies of | |
cloud_mask0[cloud_mask0 != 0] <- NA cloud_mask64[cloud_mask64 != 64] <- NA cloud_mask128[cloud_mask128 != 128] <- NA | cloud_mask0[cloud_mask0 != 0] = np.nan cloud_mask64[cloud_mask64 != 64] = np.nan cloud_mask128[cloud_mask128 != 128] = np.nan |
Sets values not matching the conditions to NaN. | |
cloud_mask_merge <- terra::merge(cloud_mask0, cloud_mask64, cloud_mask128, na.rm = TRUE) | stacked_masks = np.stack([cloud_mask128, cloud_mask64, cloud_mask0]) cloud_mask_merge = np.nanmax(stacked_masks, axis=0) |
Merges the cloud mask layers for 0, 64, and 128. | |
stk.mask <- mask(rast.stk, mask = cloud_mask_merge) | stk_mask = [np.where(~np.isnan(cloud_mask_merge), band, np.nan) for band in rast_stk] |
Applies the cloud mask to the raster stack | |
ext <- exact_extract(stk.mask,fields %>% st_transform(., crs = crs(stk.mask)),'median') | fields_transformed = fields.to_crs(crs) ext = zonal_stats(fields_transformed, stk_mask.read(1), stats=['median']) |
CONTINUE FROM HERE | |
fields = st_read("MDAEnrollment2023-2024_Buff30m.shp") | fields = gpd.read_file("MDAEnrollment2023-2024_Buff30m.shp") |
Reads the buffered shapefile. | |
plot(fields[1]) | fields.plot(edgecolor='black') |
Plots the shapefile.
|
Add Comment