I wrote a python code below in anaconda and have no issues successfully running in through the Spyder IDE.
import PySimpleGUI as sg
import os
import matplotlib.pyplot as plt
import rioxarray as rxr
import geopandas as gpd
import rasterio
from rasterio.plot import show
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
from shapely.geometry import mapping
# Define a function to plot geospatial data
def plot_geospatial_data(geotiff_path, shapefile_path, canvas_elem):
# Read GeoTIFF and Shapefile
geotiff = rasterio.open(geotiff_path)
shapefile = gpd.read_file(shapefile_path)
# Create a figure and axis for plotting with a specific size (770 x 400)
fig, ax = plt.subplots(figsize=(10.7, 5.5))
# Plot the GeoTIFF
show(geotiff, ax=ax, cmap='summer')
ax.set_title('GeoTiff/Shapefile Plot')
# Plot the Shapefile
shapefile.plot(ax=ax, edgecolor='red', alpha=0.7, legend=True, label='Shapefile')
# Show the plot on the canvas
canvas = FigureCanvasTkAgg(fig, master=canvas_elem.Widget)
canvas.draw()
canvas.get_tk_widget().pack(fill='both', expand=1)
return fig
def trim_function(existing_surface_path, fill_boundary_path, output_path_elem):
# Open the GeoTIFF using rioxarray
existing_surface = rxr.open_rasterio(existing_surface_path, masked=True).squeeze()
# Open Shapefile
aoi = os.path.join(fill_boundary_path)
# Open crop extent (your study area extent boundary)
crop_extent = gpd.read_file(aoi)
# Crop the Existing Surface using rioxarray
fill_surface = existing_surface.rio.clip(crop_extent.geometry.apply(mapping), crop_extent.crs)
# Ask the user to select an output file path
output_path = sg.popup_get_file("Select Output Path", save_as=True, file_types=(("GeoTIFF Files", "*.tif"),))
# If the user selected an output path, ensure it has a .tif extension
if output_path:
if not output_path.endswith(".tif"):
output_path += ".tif"
# Export the trimmed surface as a GeoTIFF
fill_surface.rio.to_raster(output_path, driver="GTiff")
# Define the layout of the GUI window
layout = [
[sg.Frame("Data Selection", layout=[
[
sg.Column([
[sg.Text("Select a GeoTIFF file: ", font=("Helvetica", 14))],
[sg.Text("Select a Shapefile file:", font=("Helvetica", 14))],
]),
sg.Column([
[sg.InputText(key="geotiff_path", size=(60, 1)), sg.FileBrowse(file_types=(("GeoTIFF Files", "*.tif"),))],
[sg.InputText(key="shapefile_path", size=(60, 1)), sg.FileBrowse(file_types=(("Shapefile Files", "*.shp"),))],
])
]
], expand_x=True)],
[sg.Canvas(key="canvas", background_color="white", size=(770, 400))],
[sg.Button("Plot Me", size=(10, 1), font=("Helvetica", 14)),
sg.Text("", size=(95, 1)), # Spacer
sg.Button("Export Me", size=(10, 1), font=("Helvetica", 14))
]
]
# Create the GUI window
window = sg.Window("CropMe", layout, finalize=True, size=(800, 600))
canvas_elem = window["canvas"] # Initialize canvas_elem
current_plot = None
while True:
event, values = window.read()
if event == sg.WIN_CLOSED:
break
if event == "Plot Me":
geotiff_path = values["geotiff_path"]
shapefile_path = values["shapefile_path"]
if geotiff_path and shapefile_path:
if current_plot is not None:
plt.close(current_plot)
current_plot = plot_geospatial_data(geotiff_path, shapefile_path, canvas_elem)
if event == "Export Me":
geotiff_path = values["geotiff_path"]
shapefile_path = values["shapefile_path"]
if geotiff_path and shapefile_path:
trim_function(geotiff_path, shapefile_path, window)
window.read(timeout=0) # Force a GUI refresh
window.close()
When I attempt to package the code using pyinstaller
on macOS I do the following steps:
1.) Open the terminal and run the environment: conda activate Crop_Environment
2.) Navigate to the location of my python script using the cd ~/to/python/script
3.) Run the code to compile the python script using pyinstaller pyinstaller --onefile Crop_Script.py
Opening the file it creates in the Terminal yields the following Error: ModuleNotFoundError: No module named 'rasterio.sample'
Why do I receive this error and how can I fix it?
This is due to how the rasterio package is laid out. To fix it, either explicitly add
import rasterio.sample
to your script (and iteratively every other rasterio module it will complain about afterwards), or add the rasterio modules as hidden imports to the pyinstaller build.spec
. See also this thread.