SciTools / cartopy

Cartopy - a cartographic python library with matplotlib support
https://scitools.org.uk/cartopy/docs/latest
BSD 3-Clause "New" or "Revised" License
1.41k stars 361 forks source link

Something Changed and I'm not sure what #2400

Closed HathewayWill closed 3 months ago

HathewayWill commented 3 months ago

Description

I have a yml file that uses wrf python and has some frozen versions of cartopy because wrf-python isn't compatible with current gen. Before the latest release these commands use to work.

Function to add a feature

def add_feature( ax, category, scale, facecolor, edgecolor, linewidth, name, zorder=None, alpha=None ): feature = cfeature.NaturalEarthFeature( category=category, scale=scale, facecolor=facecolor, edgecolor=edgecolor, linewidth=linewidth, name=name, zorder=zorder, alpha=alpha, ) ax.add_feature(feature)

List of features to add

features = [("physical", "10m", cfeature.COLORS["land"], "black", 0.50, "minor_islands"), ("physical", "10m", "none", "black", 0.50, "coastline"), ("physical", "10m", cfeature.COLORS["water"], None, None, "ocean_scale_rank", 2), ("physical", "10m", cfeature.COLORS["water"], cfeature.COLORS["water"], None, "lakes"), ("physical", "10m", "none", cfeature.COLORS["water"], None, "rivers_lake_centerlines"), ("cultural", "10m", "none", "grey", 1.00, "admin_1_states_provinces", None, 0.70), ("cultural", "10m", "none", "black", 1.00, "admin_0_countries"), ("cultural", "10m", "none", "black", 0.60, "admin_2_counties", None, 0.50), ("physical", "10m", "none", cfeature.COLORS["water"], None, "rivers_north_america", None, 0.75), ("physical", "10m", "none", cfeature.COLORS["water"], None, "rivers_australia", None, 0.75), ("physical", "10m", "none", cfeature.COLORS["water"], None, "rivers_europe", None, 0.75), ("physical", "10m", cfeature.COLORS["water"], cfeature.COLORS["water"], None, "lakes_north_america", None, 0.75), ("physical", "10m", cfeature.COLORS["water"], cfeature.COLORS["water"], None, "lakes_australia", None, 0.75), ("physical", "10m", cfeature.COLORS["water"], cfeature.COLORS["water"], None, "lakes_europe", None, 0.75)]

Now When I run these commands in my script i get this error.

python3 250hPa_WND_Hgt_Isotachs.py /home/workhorse/WRF_Intel/WRFV4.6.0/run/ d02

Code to reproduce

# Import standard libraries first
import os
import sys
from datetime import datetime
import glob
import re

# Import third-party libraries next
import numpy as np
import numpy.ma as ma
from scipy.ndimage import gaussian_filter
from netCDF4 import Dataset
import cartopy.crs as crs
import cartopy.feature as cfeature
import cartopy.mpl.ticker as cticker
from cartopy.mpl.gridliner import LONGITUDE_FORMATTER, LATITUDE_FORMATTER
import matplotlib.pyplot as plt
import matplotlib.colors as colors
import matplotlib.ticker as ticker
from matplotlib.cm import get_cmap
from matplotlib.animation import FuncAnimation
from PIL import Image
import metpy.calc as mpcalc
from metpy.units import units
import metpy
import xarray as xr
import geopandas as gpd

# Import WRF library last
import wrf
from wrf import ALL_TIMES, to_np

# Function to add a feature
def add_feature(
    ax, category, scale, facecolor, edgecolor, linewidth, name, zorder=None, alpha=None
):
    feature = cfeature.NaturalEarthFeature(
        category=category,
        scale=scale,
        facecolor=facecolor,
        edgecolor=edgecolor,
        linewidth=linewidth,
        name=name,
        zorder=zorder,
        alpha=alpha,
    )
    ax.add_feature(feature)

# List of features to add
features = [("physical", "10m", cfeature.COLORS["land"], "black", 0.50, "minor_islands"),
            ("physical", "10m", "none", "black", 0.50, "coastline"),
            ("physical", "10m", cfeature.COLORS["water"], None, None, "ocean_scale_rank", 2),
            ("physical", "10m", cfeature.COLORS["water"], cfeature.COLORS["water"], None, "lakes"),
            ("physical", "10m", "none", cfeature.COLORS["water"], None, "rivers_lake_centerlines"),
            ("cultural", "10m", "none", "grey", 1.00, "admin_1_states_provinces", None, 0.70),
            ("cultural", "10m", "none", "black", 1.00, "admin_0_countries"),
            ("cultural", "10m", "none", "black", 0.60, "admin_2_counties", None, 0.50),
            ("physical", "10m", "none", cfeature.COLORS["water"], None, "rivers_north_america", None, 0.75),
            ("physical", "10m", "none", cfeature.COLORS["water"], None, "rivers_australia", None, 0.75),
            ("physical", "10m", "none", cfeature.COLORS["water"], None, "rivers_europe", None, 0.75),
            ("physical", "10m", cfeature.COLORS["water"], cfeature.COLORS["water"], None, "lakes_north_america", None, 0.75),
            ("physical", "10m", cfeature.COLORS["water"], cfeature.COLORS["water"], None, "lakes_australia", None, 0.75),
            ("physical", "10m", cfeature.COLORS["water"], cfeature.COLORS["water"], None, "lakes_europe", None, 0.75)]

# Add cities to plot
cities = gpd.read_file(
    "https://naciscdn.org/naturalearth/10m/cultural/ne_10m_populated_places.zip"
)

# Ignore unnecessary errors appearing in the terminal
import warnings

warnings.filterwarnings("ignore")

# Check if the correct arguments were provided
if len(sys.argv) != 3:
    print(
        "\nEnter the two required arguments: path_wrf and domain\nFor example: script_name.py /home/WRF/test/em_real d01\n"
    )
    sys.exit()

# Define the path where the netcdf files are and the domain to be used
path_wrf = sys.argv[1]
domain = sys.argv[2]

# Define the directories where the images and animation will be saved
path_figures = "wrf_250hPa_Isotachs_Wnd_Hgt"
image_folder = os.path.join(path_figures, "Images")
animation_folder = os.path.join(path_figures, "Animation")

# Create directories for saving the figures and animation if they don't exist
for folder in [path_figures, image_folder, animation_folder]:
    if not os.path.isdir(folder):
        os.mkdir(folder)

# Loop through each WRF output file and create a plot
for ncfile_path in sorted(glob.glob(os.path.join(path_wrf, f"wrfout_{domain}*"))):
    # Read the WRF output file
    ncfile = Dataset(ncfile_path)

    # Load the WRF output file as an xarray Dataset
    ds = xr.open_dataset(ncfile_path)

    # Defining year, month, day and UTC time variables
    year = ncfile_path[
        ncfile_path.find("wrfout") + 11 : ncfile_path.find("wrfout") + 15
    ]
    month = ncfile_path[
        ncfile_path.find("wrfout") + 16 : ncfile_path.find("wrfout") + 18
    ]
    day = ncfile_path[ncfile_path.find("wrfout") + 19 : ncfile_path.find("wrfout") + 21]
    hour = ncfile_path[
        ncfile_path.find("wrfout") + 22 : ncfile_path.find("wrfout") + 24
    ]
    minute = ncfile_path[
        ncfile_path.find("wrfout") + 25 : ncfile_path.find("wrfout") + 27
    ]

    print(f"Plotting data: {year}/{month}/{day} {hour}:{minute} UTC")

    # Get the necessary variables from the netCDF file
    u = wrf.getvar(ncfile, "ua")
    v = wrf.getvar(ncfile, "va")
    p = wrf.getvar(ncfile, "pressure")
    z = wrf.getvar(ncfile, "z", units="dm")

    # Interpolate variables to 250 hPa level
    u_250 = wrf.interplevel(u, p, 250)
    v_250 = wrf.interplevel(v, p, 250)
    z_250 = wrf.interplevel(z, p, 250)
    z_250 = gaussian_filter(z_250, sigma=1)

    # Convert wind speed from m/s to knots
    u_250_knots = u_250 * 1.94384449
    v_250_knots = v_250 * 1.94384449
    wind_speed_knots = np.sqrt(u_250_knots**2 + v_250_knots**2)

    # Get latitude and longitude coordinates
    lats, lons = wrf.latlon_coords(u_250)

    # Get the Cartopy projection object
    cart_proj = wrf.get_cartopy(u_250)

    # Get the latitude and longitude coordinates from the dataset
    lats_dx = ds["XLAT"].metpy.unit_array
    lons_dy = ds["XLONG"].metpy.unit_array

    # Calculate the grid deltas
    dx, dy = mpcalc.lat_lon_grid_deltas(lons_dy, lats_dx)

    # Convert to km
    dx_km = dx.to(units.kilometer)
    dy_km = dy.to(units.kilometer)

    # Round to nearest km
    dx_km_rounded = np.round(dx_km.magnitude, decimals=2)
    dy_km_rounded = np.round(dy_km.magnitude, decimals=2)

    # Average dx & dy km value
    avg_dx_km = round(np.mean(dx_km_rounded), 2)
    avg_dy_km = round(np.mean(dy_km_rounded), 2)

    # Create a plot using cartopy and matplotlib
    fig = plt.figure(figsize=(19.2, 10.8), dpi=150)
    ax = fig.add_subplot(1, 1, 1, projection=cart_proj)

    # Determine the extent adjustment based on the average distances
    if avg_dx_km >= 9 or avg_dy_km >= 9:
        extent_adjustment = 0.50
    elif 3< avg_dx_km < 9 or 3< avg_dy_km < 9:
        extent_adjustment = 0.25
    else:
        extent_adjustment = 0.15

    # Set the map extent
    ax.set_extent(
        [lons.min() - extent_adjustment, lons.max() + extent_adjustment, 
         lats.min() - extent_adjustment, lats.max() + extent_adjustment],
        crs=crs.PlateCarree()
    )

    # Determine the label adjustment based on the average distances
    if avg_dx_km >= 9 or avg_dy_km >= 9:
        label_adjustment = 0.35
    elif 3< avg_dx_km < 9 or 3< avg_dy_km < 9:
        label_adjustment = 0.20
    else:
        label_adjustment = 0.15

    # Cartopy land feature
    ax.add_feature(cfeature.LAND, facecolor=cfeature.COLORS["land"])

    # Adding features
    for feature in features:
        add_feature(ax, *feature)

    # Define plot extent
    plot_extent = [lons.min(), lons.max(), lats.min(), lats.max()]

    # Assuming plot_extent is defined as [min_lon, max_lon, min_lat, max_lat]
    cities_within_extent = cities.cx[plot_extent[0]:plot_extent[1], plot_extent[2]:plot_extent[3]]

    # Sort cities by POP_MAX in descending order and take the first 150
    sorted_cities_within_extent = cities_within_extent.sort_values(by='POP_MAX', ascending=False).head(150)

    # Determine the minimum distance threshold based on average distances
    if avg_dx_km >= 9 or avg_dy_km >= 9:
        min_distance = 1.0
    elif 3< avg_dx_km < 9 or 3< avg_dy_km < 9:
        min_distance = 0.75
    else:
        min_distance = 0.40

    # Convert sorted cities within extent to a GeoDataFrame with a geometry column
    gdf_sorted_cities_within_extent = gpd.GeoDataFrame(sorted_cities_within_extent, geometry=gpd.points_from_xy(sorted_cities_within_extent.LONGITUDE, sorted_cities_within_extent.LATITUDE))

    # Create an empty GeoDataFrame to store cities that are far enough apart
    filtered_cities = gpd.GeoDataFrame(columns=gdf_sorted_cities_within_extent.columns)

    for index, city in gdf_sorted_cities_within_extent.iterrows():
        # Check if the city is far enough from all other cities already in the filtered list
        if not filtered_cities.empty:
            distances = filtered_cities.geometry.distance(city.geometry)
            if distances.min() >= min_distance:
                # If the city is far enough from all others, add it to the filtered list
                filtered_cities = filtered_cities.append(city)
        else:
            # If the filtered list is empty, just add the first city
            filtered_cities = filtered_cities.append(city)

    # Ensure the filtered cities GeoDataFrame has the correct geometry set
    filtered_cities = gpd.GeoDataFrame(filtered_cities, geometry='geometry')

    # Now you can plot the filtered cities
    for city, loc in zip(filtered_cities.NAME, filtered_cities.geometry):
        ax.plot(
            loc.x,
            loc.y,
            marker="o",
            markersize=3,
            color="r",
            clip_on=True,
            transform=crs.PlateCarree(),
        )
        ax.text(
            loc.x,
            loc.y,
            city,
            transform=crs.PlateCarree(),
            clip_on=True,
            ha="center",
            va="bottom",
            fontsize=8,
            color='black',
            bbox=dict(boxstyle='round,pad=0.08', facecolor='white', alpha=0.4)
        )

    # Add gridlines and labels
    gl = ax.gridlines(
        crs=crs.PlateCarree(), draw_labels=False, linestyle="--", alpha=0.5
    )
    gl.xlabels_top = False
    gl.xlabels_bottom = True
    gl.ylabels_right = False
    gl.ylabels_left = True
    gl.xformatter = LONGITUDE_FORMATTER
    gl.yformatter = LATITUDE_FORMATTER

    # Set the fixed start and end values for 250 hPa height contours
    z250_start = 700
    z250_end = 1300

    # Set the contour interval to a fixed value of 10
    contour_interval = 10

    # Create height_levels with a step of the rounded contour interval between the rounded values
    height_levels = np.arange(z250_start, z250_end, contour_interval)

    # Plot the height contours
    height_contours = ax.contour(
        lons,
        lats,
        z_250,
        levels=height_levels,
        colors="black",
        linewidths=1,
        transform=crs.PlateCarree(),
    )
    ax.clabel(height_contours, inline=True, fontsize=8, fmt="%1.0f")

    if np.max(wind_speed_knots) > 50:
        # Define fixed isotach contour levels
        isotach_levels = np.arange(50, 200, 10)

        # Define a rainbow color scheme
        rainbow_cmap = plt.get_cmap("rainbow")

        # Use the rainbow color scheme for isotach filled contours
        isotach_contourf = ax.contourf(
            lons,
            lats,
            wind_speed_knots,
            levels=isotach_levels,
            cmap=rainbow_cmap,
            extend="max",
            transform=crs.PlateCarree(),
        )
    else:
        isotach_contourf = None

    # Adding wind barbs
    # Calculate the number of grid points in the x and y dimensions
    ny, nx = np.shape(u_250_knots)

    # Define the desired number of barbs per axis
    desired_barbs = 15

    # Calculate the density of wind barbs for the domain
    barb_density_x = nx // desired_barbs
    barb_density_y = ny // desired_barbs
    barb_density = max(barb_density_x, barb_density_y)

    # Check if the calculated density is valid
    if barb_density < 1:
        barb_density = 1

    # Plot wind barbs at 250 hPa in knots
    ax.barbs(
        wrf.to_np(lons[::barb_density, ::barb_density]),
        wrf.to_np(lats[::barb_density, ::barb_density]),
        wrf.to_np(u_250_knots[::barb_density, ::barb_density]),
        wrf.to_np(v_250_knots[::barb_density, ::barb_density]),
        length=6,
        sizes=dict(emptybarb=0.25, spacing=0.2, height=0.5),
        linewidth=0.8,
        transform=crs.PlateCarree(),
    )

    if isotach_contourf is not None:
        # Add colorbar for isotachs
        cbar_isotachs = plt.colorbar(
            isotach_contourf,
            ax=ax,
            orientation="vertical",
            pad=0.05,
            aspect=50,
            extendrect=True,
        )
        cbar_isotachs.set_label("Isotachs (knots)")

    # Set plot title
    plt.title(
        f"Weather Research and Forecasting Model\nAverage Grid Spacing:{avg_dx_km}x{avg_dy_km}km\nWind Barbs at 250 hPa (knots)\nIsotachs (knots)\n250 hPa Heights (dm)",
        loc="left",
        fontsize=13,
    )
    plt.title(f"Valid: {hour}:{minute}Z {year}-{month}-{day}", loc="right", fontsize=13)

    # Save .png files
    file_out = f"wrf_{domain}_wind_250hPa_{year}{month}{day}_{hour}_{minute}.png"
    plt.savefig(
        os.path.join(path_figures, "Images", file_out), bbox_inches="tight", dpi=150
    )

    # Close the figure
    plt.close()

print("Wind barbs and isotachs at 250 hPa plot generation complete.")

# Create 'Animation' folder inside the folder with PNG files
animation_folder = os.path.join(path_figures, "Animation")
if not os.path.isdir(animation_folder):
    os.mkdir(animation_folder)

# Create files for animated .gif
png_files = [
    f for f in os.listdir(os.path.join(path_figures, "Images")) if f.endswith(".png")
]

# Sort the PNG files by date and time
png_files_datetime = []
for f in png_files:
    try:
        # Assuming you have extracted year, month, day, and hour variables as strings
        dt = datetime.strptime(f"{year}{month}{day}_{hour}_{minute}", "%Y%m%d_%H_%M")
        png_files_datetime.append(dt)
    except ValueError as e:
        print(f"Error with file {f}: {e}")
png_files_sorted = [f for _, f in sorted(zip(png_files_datetime, png_files))[1:]]

print("Creating .gif file from sorted .png files")

# Set the duration for each frame of the GIF
duration = 800

# Open the PNG files and create the GIF
images = [
    Image.open(os.path.join(image_folder, filename)) for filename in png_files_sorted
]
gif_file_out = f"wrf_{domain}_250hPa_WIND_Hgt_Isotachs.gif"
gif_path = os.path.join(animation_folder, gif_file_out)
images[0].save(
    gif_path, save_all=True, append_images=images[1:], duration=duration, loop=0
)

print("GIF generation complete.")

Traceback

Traceback (most recent call last):
  File "fiona/ogrext.pyx", line 136, in fiona.ogrext.gdal_open_vector
  File "fiona/_err.pyx", line 291, in fiona._err.exc_wrap_pointer
fiona._err.CPLE_OpenFailedError: Unable to open /home/workhorse/.local/share/cartopy/shapefiles/natural_earth/cultural/ne_10m_admin_1_states_provinces.shx or /home/workhorse/.local/share/cartopy/shapefiles/natural_earth/cultural/ne_10m_admin_1_states_provinces.SHX. Set SHAPE_RESTORE_SHX config option to YES to restore or create it.

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/home/workhorse/Desktop/python charts that work/250hPa_WND_Hgt_Isotachs.py", line 369, in <module>
    plt.savefig(
  File "/home/workhorse/WRF_Intel/miniconda3/envs/wrf-python/lib/python3.11/site-packages/matplotlib/pyplot.py", line 959, in savefig
    res = fig.savefig(*args, **kwargs)
          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/workhorse/WRF_Intel/miniconda3/envs/wrf-python/lib/python3.11/site-packages/matplotlib/figure.py", line 3285, in savefig
    self.canvas.print_figure(fname, **kwargs)
  File "/home/workhorse/WRF_Intel/miniconda3/envs/wrf-python/lib/python3.11/site-packages/matplotlib/backends/backend_qtagg.py", line 82, in print_figure
    super().print_figure(*args, **kwargs)
  File "/home/workhorse/WRF_Intel/miniconda3/envs/wrf-python/lib/python3.11/site-packages/matplotlib/backend_bases.py", line 2314, in print_figure
    self.figure.draw(renderer)
  File "/home/workhorse/WRF_Intel/miniconda3/envs/wrf-python/lib/python3.11/site-packages/matplotlib/artist.py", line 74, in draw_wrapper
    result = draw(artist, renderer, *args, **kwargs)
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/workhorse/WRF_Intel/miniconda3/envs/wrf-python/lib/python3.11/site-packages/matplotlib/artist.py", line 51, in draw_wrapper
    return draw(artist, renderer)
           ^^^^^^^^^^^^^^^^^^^^^^
  File "/home/workhorse/WRF_Intel/miniconda3/envs/wrf-python/lib/python3.11/site-packages/matplotlib/figure.py", line 3082, in draw
    mimage._draw_list_compositing_images(
  File "/home/workhorse/WRF_Intel/miniconda3/envs/wrf-python/lib/python3.11/site-packages/matplotlib/image.py", line 131, in _draw_list_compositing_images
    a.draw(renderer)
  File "/home/workhorse/WRF_Intel/miniconda3/envs/wrf-python/lib/python3.11/site-packages/matplotlib/artist.py", line 51, in draw_wrapper
    return draw(artist, renderer)
           ^^^^^^^^^^^^^^^^^^^^^^
  File "/home/workhorse/WRF_Intel/miniconda3/envs/wrf-python/lib/python3.11/site-packages/cartopy/mpl/geoaxes.py", line 535, in draw
    return super().draw(renderer=renderer, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/workhorse/WRF_Intel/miniconda3/envs/wrf-python/lib/python3.11/site-packages/matplotlib/artist.py", line 51, in draw_wrapper
    return draw(artist, renderer)
           ^^^^^^^^^^^^^^^^^^^^^^
  File "/home/workhorse/WRF_Intel/miniconda3/envs/wrf-python/lib/python3.11/site-packages/matplotlib/axes/_base.py", line 3100, in draw
    mimage._draw_list_compositing_images(
  File "/home/workhorse/WRF_Intel/miniconda3/envs/wrf-python/lib/python3.11/site-packages/matplotlib/image.py", line 131, in _draw_list_compositing_images
    a.draw(renderer)
  File "/home/workhorse/WRF_Intel/miniconda3/envs/wrf-python/lib/python3.11/site-packages/matplotlib/artist.py", line 51, in draw_wrapper
    return draw(artist, renderer)
           ^^^^^^^^^^^^^^^^^^^^^^
  File "/home/workhorse/WRF_Intel/miniconda3/envs/wrf-python/lib/python3.11/site-packages/cartopy/mpl/feature_artist.py", line 152, in draw
    geoms = self._feature.intersecting_geometries(extent)
            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/workhorse/WRF_Intel/miniconda3/envs/wrf-python/lib/python3.11/site-packages/cartopy/feature/__init__.py", line 305, in intersecting_geometries
    return super().intersecting_geometries(extent)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/workhorse/WRF_Intel/miniconda3/envs/wrf-python/lib/python3.11/site-packages/cartopy/feature/__init__.py", line 108, in intersecting_geometries
    return (geom for geom in self.geometries() if
                             ^^^^^^^^^^^^^^^^^
  File "/home/workhorse/WRF_Intel/miniconda3/envs/wrf-python/lib/python3.11/site-packages/cartopy/feature/__init__.py", line 290, in geometries
    geometries = tuple(shapereader.Reader(path).geometries())
                       ^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/workhorse/WRF_Intel/miniconda3/envs/wrf-python/lib/python3.11/site-packages/cartopy/io/shapereader.py", line 192, in __init__
    with fiona.open(filename) as f:
         ^^^^^^^^^^^^^^^^^^^^
  File "/home/workhorse/WRF_Intel/miniconda3/envs/wrf-python/lib/python3.11/site-packages/fiona/env.py", line 457, in wrapper
    return f(*args, **kwds)
           ^^^^^^^^^^^^^^^^
  File "/home/workhorse/WRF_Intel/miniconda3/envs/wrf-python/lib/python3.11/site-packages/fiona/__init__.py", line 292, in open
    colxn = Collection(
            ^^^^^^^^^^^
  File "/home/workhorse/WRF_Intel/miniconda3/envs/wrf-python/lib/python3.11/site-packages/fiona/collection.py", line 243, in __init__
    self.session.start(self, **kwargs)
  File "fiona/ogrext.pyx", line 588, in fiona.ogrext.Session.start
  File "fiona/ogrext.pyx", line 143, in fiona.ogrext.gdal_open_vector
fiona.errors.DriverError: Unable to open /home/workhorse/.local/share/cartopy/shapefiles/natural_earth/cultural/ne_10m_admin_1_states_provinces.shx or /home/workhorse/.local/share/cartopy/shapefiles/natural_earth/cultural/ne_10m_admin_1_states_provinces.SHX. Set SHAPE_RESTORE_SHX config option to YES to restore or create it.
Full environment definition ### Operating system Ubuntu 22.04 ### Cartopy version ### conda list ``` # Name Version Build Channel _libgcc_mutex 0.1 conda_forge conda-forge _openmp_mutex 4.5 2_gnu conda-forge alsa-lib 1.2.8 h166bdaf_0 conda-forge arrow-cpp 11.0.0 ha770c72_13_cpu conda-forge attr 2.5.1 h166bdaf_1 conda-forge attrs 23.1.0 pypi_0 pypi aws-c-auth 0.6.26 hf365957_1 conda-forge aws-c-cal 0.5.21 h48707d8_2 conda-forge aws-c-common 0.8.14 h0b41bf4_0 conda-forge aws-c-compression 0.2.16 h03acc5a_5 conda-forge aws-c-event-stream 0.2.20 h00877a2_4 conda-forge aws-c-http 0.7.6 hf342b9f_0 conda-forge aws-c-io 0.13.19 h5b20300_3 conda-forge aws-c-mqtt 0.8.6 hc4349f7_12 conda-forge aws-c-s3 0.2.7 h909e904_1 conda-forge aws-c-sdkutils 0.1.8 h03acc5a_0 conda-forge aws-checksums 0.1.14 h03acc5a_5 conda-forge aws-crt-cpp 0.19.8 hf7fbfca_12 conda-forge aws-sdk-cpp 1.10.57 h17c43bd_8 conda-forge basemap 1.3.6 py311h3b0d061_2 conda-forge basemap-data 1.3.2 pyhd8ed1ab_3 conda-forge blosc 1.21.5 h0f2a231_0 conda-forge bokeh 3.2.2 pyhd8ed1ab_0 conda-forge boost-cpp 1.78.0 h5adbc97_2 conda-forge brotli 1.0.9 h166bdaf_8 conda-forge brotli-bin 1.0.9 h166bdaf_8 conda-forge brotli-python 1.0.9 py311ha362b79_8 conda-forge brotlipy 0.7.0 py311hd4cff14_1005 conda-forge bzip2 1.0.8 h7f98852_4 conda-forge c-ares 1.19.1 hd590300_0 conda-forge ca-certificates 2023.7.22 hbcca054_0 conda-forge cairo 1.16.0 ha61ee94_1014 conda-forge cartopy 0.22.0 py311h320fe9a_0 conda-forge certifi 2023.7.22 pyhd8ed1ab_0 conda-forge cffi 1.15.1 py311h409f033_3 conda-forge cfitsio 4.2.0 hd9d235c_0 conda-forge cftime 1.6.2 py311h4c7f6c3_1 conda-forge charset-normalizer 3.2.0 pyhd8ed1ab_0 conda-forge click 8.1.7 unix_pyh707e725_0 conda-forge click-plugins 1.1.1 pypi_0 pypi cligj 0.7.2 pypi_0 pypi cloudpickle 2.2.1 pyhd8ed1ab_0 conda-forge contourpy 1.1.0 py311h9547e67_0 conda-forge cryptography 41.0.3 py311h63ff55d_0 conda-forge curl 7.88.1 hdc1c0ab_1 conda-forge cycler 0.11.0 pyhd8ed1ab_0 conda-forge cytoolz 0.12.2 py311h459d7ec_0 conda-forge dask 2023.9.1 pyhd8ed1ab_0 conda-forge dask-core 2023.9.1 pyhd8ed1ab_0 conda-forge dbus 1.13.6 h5008d03_3 conda-forge distributed 2023.9.1 pyhd8ed1ab_0 conda-forge esmf 8.4.1 nompi_he2e5181_0 conda-forge expat 2.5.0 hcb278e6_1 conda-forge fftw 3.3.10 nompi_hc118613_108 conda-forge fiona 1.9.5 pypi_0 pypi font-ttf-dejavu-sans-mono 2.37 hab24e00_0 conda-forge font-ttf-inconsolata 3.000 h77eed37_0 conda-forge font-ttf-source-code-pro 2.038 h77eed37_0 conda-forge font-ttf-ubuntu 0.83 hab24e00_0 conda-forge fontconfig 2.14.2 h14ed4e7_0 conda-forge fonts-conda-ecosystem 1 0 conda-forge fonts-conda-forge 1 0 conda-forge fonttools 4.42.1 py311h459d7ec_0 conda-forge freeglut 3.2.2 h9c3ff4c_1 conda-forge freetype 2.12.1 h267a509_2 conda-forge freexl 1.0.6 h166bdaf_1 conda-forge fsspec 2023.9.0 pyh1a96a4e_0 conda-forge g2clib 1.6.0 3 conda-forge geopandas 0.14.0 pypi_0 pypi geos 3.11.1 h27087fc_0 conda-forge geotiff 1.7.1 h7a142b4_6 conda-forge gettext 0.21.1 h27087fc_0 conda-forge gflags 2.2.2 he1b5a44_1004 conda-forge giflib 5.2.1 h0b41bf4_3 conda-forge glib 2.78.0 hfc55251_0 conda-forge glib-tools 2.78.0 hfc55251_0 conda-forge glog 0.6.0 h6f12383_0 conda-forge graphite2 1.3.13 h58526e2_1001 conda-forge gsl 2.7 he838d99_0 conda-forge gst-plugins-base 1.22.0 h4243ec0_2 conda-forge gstreamer 1.22.0 h25f0c4b_2 conda-forge gstreamer-orc 0.4.34 hd590300_0 conda-forge harfbuzz 6.0.0 h8e241bc_0 conda-forge hdf4 4.2.15 h9772cbc_5 conda-forge hdf5 1.12.2 nompi_h4df4325_101 conda-forge hdfeos2 2.20 hb955811_1002 conda-forge hdfeos5 5.1.16 hc55c121_12 conda-forge heapdict 1.0.1 py_0 conda-forge icu 70.1 h27087fc_0 conda-forge idna 3.4 pyhd8ed1ab_0 conda-forge imageio 2.31.1 pyh24c5eb1_0 conda-forge importlib-metadata 6.8.0 pyha770c72_0 conda-forge importlib_metadata 6.8.0 hd8ed1ab_0 conda-forge importlib_resources 6.0.1 pyhd8ed1ab_0 conda-forge jack 1.9.22 h11f4161_0 conda-forge jasper 2.0.33 h0ff4b12_1 conda-forge jinja2 3.1.2 pyhd8ed1ab_1 conda-forge jpeg 9e h0b41bf4_3 conda-forge json-c 0.16 hc379101_0 conda-forge kealib 1.5.0 ha7026e8_0 conda-forge keyutils 1.6.1 h166bdaf_0 conda-forge kiwisolver 1.4.5 py311h9547e67_0 conda-forge krb5 1.20.1 h81ceb04_0 conda-forge lame 3.100 h166bdaf_1003 conda-forge lcms2 2.15 hfd0df8a_0 conda-forge ld_impl_linux-64 2.40 h41732ed_0 conda-forge lerc 4.0.0 h27087fc_0 conda-forge libabseil 20230125.0 cxx17_hcb278e6_1 conda-forge libaec 1.0.6 hcb278e6_1 conda-forge libarrow 11.0.0 h93537a5_13_cpu conda-forge libblas 3.9.0 18_linux64_openblas conda-forge libbrotlicommon 1.0.9 h166bdaf_8 conda-forge libbrotlidec 1.0.9 h166bdaf_8 conda-forge libbrotlienc 1.0.9 h166bdaf_8 conda-forge libcap 2.67 he9d0100_0 conda-forge libcblas 3.9.0 18_linux64_openblas conda-forge libclang 15.0.7 default_had23c3d_1 conda-forge libclang13 15.0.7 default_h3e3d535_1 conda-forge libcrc32c 1.1.2 h9c3ff4c_0 conda-forge libcups 2.3.3 h36d4200_3 conda-forge libcurl 7.88.1 hdc1c0ab_1 conda-forge libdb 6.2.32 h9c3ff4c_0 conda-forge libdeflate 1.17 h0b41bf4_0 conda-forge libedit 3.1.20191231 he28a2e2_2 conda-forge libev 4.33 h516909a_1 conda-forge libevent 2.1.10 h28343ad_4 conda-forge libexpat 2.5.0 hcb278e6_1 conda-forge libffi 3.4.2 h7f98852_5 conda-forge libflac 1.4.3 h59595ed_0 conda-forge libgcc-ng 13.2.0 h807b86a_0 conda-forge libgcrypt 1.10.1 h166bdaf_0 conda-forge libgdal 3.6.2 h6c674c2_9 conda-forge libgfortran-ng 13.2.0 h69a702a_0 conda-forge libgfortran5 13.2.0 ha4646dd_0 conda-forge libglib 2.78.0 hebfc3b9_0 conda-forge libglu 9.0.0 he1b5a44_1001 conda-forge libgomp 13.2.0 h807b86a_0 conda-forge libgoogle-cloud 2.8.0 h0bc5f78_1 conda-forge libgpg-error 1.47 h71f35ed_0 conda-forge libgrpc 1.52.1 hcf146ea_1 conda-forge libiconv 1.17 h166bdaf_0 conda-forge libkml 1.3.0 h37653c0_1015 conda-forge liblapack 3.9.0 18_linux64_openblas conda-forge libllvm15 15.0.7 hadd5161_1 conda-forge libnetcdf 4.9.1 nompi_h34a3ff0_101 conda-forge libnghttp2 1.52.0 h61bc06f_0 conda-forge libnsl 2.0.0 h7f98852_0 conda-forge libnuma 2.0.16 h0b41bf4_1 conda-forge libogg 1.3.4 h7f98852_1 conda-forge libopenblas 0.3.24 pthreads_h413a1c8_0 conda-forge libopus 1.3.1 h7f98852_1 conda-forge libpng 1.6.39 h753d276_0 conda-forge libpq 15.2 hb675445_0 conda-forge libprotobuf 3.21.12 hfc55251_2 conda-forge librttopo 1.1.0 ha49c73b_12 conda-forge libsndfile 1.2.2 hbc2eb40_0 conda-forge libspatialite 5.0.1 h221c8f1_23 conda-forge libsqlite 3.43.0 h2797004_0 conda-forge libssh2 1.11.0 h0841786_0 conda-forge libstdcxx-ng 13.2.0 h7e041cc_0 conda-forge libsystemd0 253 h8c4010b_1 conda-forge libthrift 0.18.1 h5e4af38_0 conda-forge libtiff 4.5.0 h6adf6a1_2 conda-forge libtool 2.4.7 h27087fc_0 conda-forge libudev1 253 h0b41bf4_1 conda-forge libutf8proc 2.8.0 h166bdaf_0 conda-forge libuuid 2.38.1 h0b41bf4_0 conda-forge libvorbis 1.3.7 h9c3ff4c_0 conda-forge libwebp-base 1.3.1 hd590300_0 conda-forge libxcb 1.13 h7f98852_1004 conda-forge libxkbcommon 1.5.0 h79f4944_1 conda-forge libxml2 2.10.3 hca2bb57_4 conda-forge libzip 1.10.1 h2629f0a_3 conda-forge libzlib 1.2.13 hd590300_5 conda-forge locket 1.0.0 pyhd8ed1ab_0 conda-forge lz4 4.3.2 py311h9f220a4_0 conda-forge lz4-c 1.9.4 hcb278e6_0 conda-forge markupsafe 2.1.3 py311h459d7ec_0 conda-forge matplotlib 3.6.3 py311h38be061_0 conda-forge matplotlib-base 3.6.3 py311h8597a09_0 conda-forge metpy 1.5.1 pyhd8ed1ab_0 conda-forge mpg123 1.31.3 hcb278e6_0 conda-forge msgpack-python 1.0.5 py311ha3edf6b_0 conda-forge munkres 1.1.4 pyh9f0ad1d_0 conda-forge mysql-common 8.0.33 hf1915f5_4 conda-forge mysql-libs 8.0.33 hca2cd23_4 conda-forge ncl 6.6.2 ha851dbf_44 conda-forge ncurses 6.4 hcb278e6_0 conda-forge netcdf-fortran 4.6.0 nompi_heb5813c_103 conda-forge netcdf4 1.6.3 nompi_py311ha396515_100 conda-forge nspr 4.35 h27087fc_0 conda-forge nss 3.92 h1d7d5a4_0 conda-forge numpy 1.24.2 pypi_0 pypi openjpeg 2.5.0 hfec8fc6_2 conda-forge openssl 3.1.2 hd590300_0 conda-forge orc 1.8.3 h2f23424_1 conda-forge packaging 23.1 pyhd8ed1ab_0 conda-forge pandas 1.5.3 pypi_0 pypi parquet-cpp 1.5.1 2 conda-forge partd 1.4.0 pyhd8ed1ab_1 conda-forge pcre2 10.40 hc3806b6_0 conda-forge pillow 9.4.0 py311h50def17_1 conda-forge pint 0.22 pyhd8ed1ab_1 conda-forge pip 23.0.1 pypi_0 pypi pixman 0.40.0 h36c2ea0_0 conda-forge platformdirs 3.2.0 pypi_0 pypi ply 3.11 py_1 conda-forge pooch 1.7.0 pyha770c72_3 conda-forge poppler 23.03.0 h091648b_0 conda-forge poppler-data 0.4.12 hd8ed1ab_0 conda-forge postgresql 15.2 h3248436_0 conda-forge proj 9.1.1 h8ffa02c_2 conda-forge psutil 5.9.5 py311h2582759_0 conda-forge pthread-stubs 0.4 h36c2ea0_1001 conda-forge pulseaudio 16.1 hcb278e6_3 conda-forge pulseaudio-client 16.1 h5195f5e_3 conda-forge pulseaudio-daemon 16.1 ha8d29e2_3 conda-forge pyarrow 11.0.0 py311hbdf6286_13_cpu conda-forge pycparser 2.21 pyhd8ed1ab_0 conda-forge pyngl 1.6.1 py311hd9db0c4_6 conda-forge pynio 1.5.5 py311h5956141_22 conda-forge pyopenssl 23.2.0 pyhd8ed1ab_1 conda-forge pyparsing 3.1.1 pyhd8ed1ab_0 conda-forge pyproj 3.4.1 py311h945b3ca_1 conda-forge pyqt 5.15.9 py311hf0fb5b6_4 conda-forge pyqt5-sip 12.12.2 py311hb755f60_4 conda-forge pyshp 2.3.1 pyhd8ed1ab_0 conda-forge pysocks 1.7.1 pyha2e5f31_6 conda-forge python 3.11.5 hab00c5b_0_cpython conda-forge python-dateutil 2.8.2 pyhd8ed1ab_0 conda-forge python-tzdata 2023.3 pyhd8ed1ab_0 conda-forge python_abi 3.11 3_cp311 conda-forge pytz 2023.3.post1 pyhd8ed1ab_0 conda-forge pyyaml 6.0.1 py311h459d7ec_0 conda-forge qt-main 5.15.8 h5d23da1_6 conda-forge rdma-core 28.9 h59595ed_1 conda-forge re2 2023.02.02 hcb278e6_0 conda-forge readline 8.2 h8228510_1 conda-forge requests 2.28.2 pypi_0 pypi s2n 1.3.41 h3358134_0 conda-forge scipy 1.11.2 py311h64a7726_1 conda-forge setuptools 67.7.2 pypi_0 pypi shapely 2.0.1 py311h0f577a2_0 conda-forge sip 6.7.11 py311hb755f60_0 conda-forge six 1.16.0 pyh6c4a22f_0 conda-forge snappy 1.1.10 h9fff704_0 conda-forge sortedcontainers 2.4.0 pyhd8ed1ab_0 conda-forge sqlite 3.43.0 h2c6b66d_0 conda-forge tblib 1.7.0 pyhd8ed1ab_0 conda-forge tiledb 2.13.2 hd532e3d_0 conda-forge tk 8.6.12 h27826a3_0 conda-forge toml 0.10.2 pyhd8ed1ab_0 conda-forge tomli 2.0.1 pyhd8ed1ab_0 conda-forge toolz 0.12.0 pyhd8ed1ab_0 conda-forge tornado 6.3.3 py311h459d7ec_0 conda-forge traitlets 5.9.0 pyhd8ed1ab_0 conda-forge typing-extensions 4.7.1 hd8ed1ab_0 conda-forge typing_extensions 4.7.1 pyha770c72_0 conda-forge tzcode 2023c h0b41bf4_0 conda-forge tzdata 2023c h71feb2d_0 conda-forge ucx 1.14.1 h64cca9d_4 conda-forge udunits2 2.2.28 hc3e0081_0 conda-forge urllib3 1.26.17 pypi_0 pypi wget 3.2 pypi_0 pypi wheel 0.41.2 pyhd8ed1ab_0 conda-forge wrapt 1.15.0 py311h2582759_0 conda-forge wrf-python 1.3.4.1 py311ha9cf758_3 conda-forge xarray 2023.3.0 pypi_0 pypi xcb-util 0.4.0 h166bdaf_0 conda-forge xcb-util-image 0.4.0 h166bdaf_0 conda-forge xcb-util-keysyms 0.4.0 h166bdaf_0 conda-forge xcb-util-renderutil 0.3.9 h166bdaf_0 conda-forge xcb-util-wm 0.4.1 h166bdaf_0 conda-forge xerces-c 3.2.4 h55805fa_1 conda-forge xkeyboard-config 2.38 h0b41bf4_0 conda-forge xorg-fixesproto 5.0 h7f98852_1002 conda-forge xorg-imake 1.0.7 0 conda-forge xorg-inputproto 2.3.2 h7f98852_1002 conda-forge xorg-kbproto 1.0.7 h7f98852_1002 conda-forge xorg-libice 1.0.10 h7f98852_0 conda-forge xorg-libsm 1.2.3 hd9c2040_1000 conda-forge xorg-libx11 1.8.4 h0b41bf4_0 conda-forge xorg-libxau 1.0.11 hd590300_0 conda-forge xorg-libxaw 1.0.14 h7f98852_1 conda-forge xorg-libxdmcp 1.1.3 h7f98852_0 conda-forge xorg-libxext 1.3.4 h0b41bf4_2 conda-forge xorg-libxfixes 5.0.3 h7f98852_1004 conda-forge xorg-libxi 1.7.10 h7f98852_0 conda-forge xorg-libxmu 1.1.3 h7f98852_0 conda-forge xorg-libxpm 3.5.16 hd590300_0 conda-forge xorg-libxrender 0.9.10 h7f98852_1003 conda-forge xorg-libxt 1.3.0 hd590300_0 conda-forge xorg-makedepend 1.0.8 h59595ed_0 conda-forge xorg-renderproto 0.11.1 h7f98852_1002 conda-forge xorg-xextproto 7.3.0 h0b41bf4_1003 conda-forge xorg-xproto 7.0.31 h7f98852_1007 conda-forge xyzservices 2023.7.0 pyhd8ed1ab_0 conda-forge xz 5.2.6 h166bdaf_0 conda-forge yaml 0.2.5 h7f98852_2 conda-forge zict 3.0.0 pyhd8ed1ab_0 conda-forge zipp 3.16.2 pyhd8ed1ab_0 conda-forge zlib 1.2.13 hd590300_5 conda-forge zstd 1.5.5 hfc55251_0 conda-forge ``` ### pip list ``` ------------------- ------------ attrs 23.1.0 basemap 1.3.6 basemap-data 1.3.2 bokeh 3.2.2 Brotli 1.0.9 brotlipy 0.7.0 Cartopy 0.22.0 certifi 2023.7.22 cffi 1.15.1 cftime 1.6.2 charset-normalizer 3.2.0 click 8.1.7 click-plugins 1.1.1 cligj 0.7.2 cloudpickle 2.2.1 contourpy 1.1.0 cryptography 41.0.3 cycler 0.11.0 cytoolz 0.12.2 dask 2023.9.1 distributed 2023.9.1 fiona 1.9.5 fonttools 4.42.1 fsspec 2023.9.0 geopandas 0.14.0 HeapDict 1.0.1 idna 3.4 imageio 2.31.1 importlib-metadata 6.8.0 importlib-resources 6.0.1 Jinja2 3.1.2 kiwisolver 1.4.5 locket 1.0.0 lz4 4.3.2 MarkupSafe 2.1.3 matplotlib 3.6.3 MetPy 1.5.1 msgpack 1.0.5 munkres 1.1.4 netCDF4 1.6.3 ngl 1.6.1 numpy 1.24.2 packaging 23.1 pandas 1.5.3 partd 1.4.0 Pillow 9.4.0 Pint 0.22 pip 23.0.1 platformdirs 3.2.0 ply 3.11 pooch 1.7.0 psutil 5.9.5 pyarrow 11.0.0 pycparser 2.21 PyNIO 1.5.5 pyOpenSSL 23.2.0 pyparsing 3.1.1 pyproj 3.4.1 PyQt5 5.15.9 PyQt5-sip 12.12.2 pyshp 2.3.1 PySocks 1.7.1 python-dateutil 2.8.2 pytz 2023.3.post1 PyYAML 6.0.1 requests 2.28.2 scipy 1.11.2 setuptools 67.7.2 shapely 2.0.1 sip 6.7.11 six 1.16.0 sortedcontainers 2.4.0 tblib 1.7.0 toml 0.10.2 tomli 2.0.1 toolz 0.12.0 tornado 6.3.3 traitlets 5.9.0 typing_extensions 4.7.1 tzdata 2023.3 urllib3 1.26.17 wget 3.2 wheel 0.41.2 wrapt 1.15.0 wrf-python 1.3.4.1 xarray 2023.3.0 xyzservices 2023.7.0 zict 3.0.0 zipp 3.16.2 ```

files.zip

greglucas commented 3 months ago

This looks like an error in Fiona, I'm not sure why it is an issue with Cartopy?

fiona.errors.DriverError: Unable to open /home/workhorse/.local/share/cartopy/shapefiles/natural_earth/cultural/ne_10m_admin_1_states_provinces.shx or /home/workhorse/.local/share/cartopy/shapefiles/natural_earth/cultural/ne_10m_admin_1_states_provinces.SHX. Set SHAPE_RESTORE_SHX config option to YES to restore or create it.

We need minimal reproducible examples if it has to do with Cartopy. It would help to just add a single feature onto a map if that is causing the issues rather than plotting everything else in your script so you can try to narrow it down to what is causing the issues.

HathewayWill commented 3 months ago

Okay, so I commented out the line about states and provinces and it runs.

# List of features to add
features = [("physical", "10m", cfeature.COLORS["land"], "black", 0.50, "minor_islands"),
            ("physical", "10m", "none", "black", 0.50, "coastline"),
            ("physical", "10m", cfeature.COLORS["water"], None, None, "ocean_scale_rank", 2),
            ("physical", "10m", cfeature.COLORS["water"], cfeature.COLORS["water"], None, "lakes"),
            ("physical", "10m", "none", cfeature.COLORS["water"], None, "rivers_lake_centerlines"),
            #("cultural", "10m", "none", "grey", 1.00, "admin_1_states_provinces", None, 0.70),
            ("cultural", "10m", "none", "black", 1.00, "admin_0_countries"),
            ("cultural", "10m", "none", "black", 0.60, "admin_2_counties", None, 0.50),
            ("physical", "10m", "none", cfeature.COLORS["water"], None, "rivers_north_america", None, 0.75),
            ("physical", "10m", "none", cfeature.COLORS["water"], None, "rivers_australia", None, 0.75),
            ("physical", "10m", "none", cfeature.COLORS["water"], None, "rivers_europe", None, 0.75),
            ("physical", "10m", cfeature.COLORS["water"], cfeature.COLORS["water"], None, "lakes_north_america", None, 0.75),
            ("physical", "10m", cfeature.COLORS["water"], cfeature.COLORS["water"], None, "lakes_australia", None, 0.75),
            ("physical", "10m", cfeature.COLORS["water"], cfeature.COLORS["water"], None, "lakes_europe", None, 0.75)]

I think what it is, and i'm not 100% sure how to test this, is that the natural earth database may have changed their website path to the file. Because the 50m resolution works but the 10m resolution does not. So the cartopy.NaturalEarth is looking in the wrong place.

greglucas commented 3 months ago

This minimal script works for me with that feature. Are you able to get this to work?

import cartopy.crs as ccrs
import cartopy.feature as cfeature
import matplotlib.pyplot as plt

fig = plt.figure()
ax = fig.add_subplot(1, 1, 1, projection=ccrs.PlateCarree())

feat = cfeature.NaturalEarthFeature(category='cultural', scale="10m", name='admin_1_states_provinces')

ax.add_feature(feat, edgecolor='black', facecolor='none')

plt.show()
rcomer commented 3 months ago

It may be worth just deleting your natural_earth directory and starting again, so that the relevant files get downloaded from the current location. Natural Earth did indeed move to a different server/website a few years ago. Going back a lot further, the naming convention of these files changed.

HathewayWill commented 3 months ago

It may be worth just deleting your natural_earth directory and starting again, so that the relevant files get downloaded from the current location. Natural Earth did indeed move to a different server/website a few years ago. Going back a lot further, the naming convention of these files changed.

@rcomer That seems to be the issue, the files in the .local directory got corrupted somehow. By deleting it and reinstalling it worked.

@greglucas sorry for the useless issue.

rcomer commented 3 months ago

@HathewayWill glad you got it sorted! I'll close this issue then.