Repository: geopandas/geopandas

Issue:
ENH: Function to show layers in a particular file
As far as I can tell, the only way to see the layers in a particular file is to read the file first, e.g.

```python
import geopandas as gpd

# read the GeoPackage file
gdf = gpd.read_file('your_geopackage_file.gpkg')

# list the layer names
layer_names = list(gdf.name)
print(layer_names)
```

Would it be possible/useful to add a function that takes the file name as the first argument and returns the layers? This from the `sf` package could provide some inspiration: https://r-spatial.github.io/sf/reference/st_layers.html

Apologies in the like event I've missed something, fairly new to this space!

Discussion:

Patch:
diff --git a/doc/source/community/ecosystem.md b/doc/source/community/ecosystem.md
index 1ff83cdb..0065aa2f 100644
--- a/doc/source/community/ecosystem.md
+++ b/doc/source/community/ecosystem.md
@@ -229,6 +229,14 @@ distribution of observations by color in a given map. These distributional
 visualizations for map classification schemes assist in analytical cartography and
 spatial data visualization.
 
+#### [buckaroo](https://github.com/paddymul/buckaroo)
+`buckaroo` is a modern data table for Jupyter that expedites the most
+common exploratory data analysis tasks. It provides scrollable tables,
+histograms, and summary stats. Buckaroo supports many DataFrame
+libraries including `geopandas`. It can display `GeoDataFrame`s as
+tables, it also supports rendering the Geometry as an SVG in the
+table.
+
 ### Geometry manipulation
 
 #### [TopoJSON](https://github.com/mattijn/topojson)
diff --git a/doc/source/docs/reference/io.rst b/doc/source/docs/reference/io.rst
index f5d77aed..8c5a8ac9 100644
--- a/doc/source/docs/reference/io.rst
+++ b/doc/source/docs/reference/io.rst
@@ -8,6 +8,7 @@ GIS vector files
 .. autosummary::
    :toctree: api/
 
+   list_layers
    read_file
    GeoDataFrame.to_file
 
diff --git a/doc/source/docs/user_guide/io.rst b/doc/source/docs/user_guide/io.rst
index 73f613c7..2e659a39 100644
--- a/doc/source/docs/user_guide/io.rst
+++ b/doc/source/docs/user_guide/io.rst
@@ -32,6 +32,9 @@ the ``layer`` keyword::
 
     countries_gdf = geopandas.read_file("package.gpkg", layer='countries')
 
+If you have a file with multiple layers, you can list them using
+:func:`geopandas.list_layers`. Note that this function requires Pyogrio.
+
 GeoPandas can also load resources directly from
 a web URL, for example for GeoJSON files from `geojson.xyz <http://geojson.xyz/>`_::
 
diff --git a/geopandas/__init__.py b/geopandas/__init__.py
index bbe1b445..42994e56 100644
--- a/geopandas/__init__.py
+++ b/geopandas/__init__.py
@@ -5,6 +5,7 @@ from geopandas.geodataframe import GeoDataFrame
 from geopandas.array import points_from_xy
 
 from geopandas.io.file import _read_file as read_file
+from geopandas.io.file import _list_layers as list_layers
 from geopandas.io.arrow import _read_parquet as read_parquet
 from geopandas.io.arrow import _read_feather as read_feather
 from geopandas.io.sql import _read_postgis as read_postgis
diff --git a/geopandas/io/file.py b/geopandas/io/file.py
index f4d8262b..aff36857 100644
--- a/geopandas/io/file.py
+++ b/geopandas/io/file.py
@@ -796,3 +796,34 @@ def _geometry_types(df):
         geom_types = geom_types[0]
 
     return geom_types
+
+
+def _list_layers(filename) -> pd.DataFrame:
+    """List layers available in a file.
+
+    Provides an overview of layers available in a file or URL together with their
+    geometry types. When supported by the data source, this includes both spatial and
+    non-spatial layers. Non-spatial layers are indicated by the ``"geometry_type"``
+    column being ``None``. GeoPandas will not read such layers but they can be read into
+    a pd.DataFrame using :func:`pyogrio.read_dataframe`.
+
+    Parameters
+    ----------
+    filename : str, path object or file-like object
+        Either the absolute or relative path to the file or URL to
+        be opened, or any object with a read() method (such as an open file
+        or StringIO)
+
+    Returns
+    -------
+    pandas.DataFrame
+        A DataFrame with columns "name" and "geometry_type" and one row per layer.
+    """
+    _import_pyogrio()
+    _check_pyogrio("list_layers")
+
+    import pyogrio
+
+    return pd.DataFrame(
+        pyogrio.list_layers(filename), columns=["name", "geometry_type"]
+    )


Test patch:
diff --git a/geopandas/io/tests/test_file.py b/geopandas/io/tests/test_file.py
index cd1475fd..efd09ad5 100644
--- a/geopandas/io/tests/test_file.py
+++ b/geopandas/io/tests/test_file.py
@@ -1304,3 +1304,24 @@ def test_option_io_engine(nybb_filename):
     finally:
         fiona.supported_drivers["ESRI Shapefile"] = orig
         geopandas.options.io_engine = None
+
+
+@PYOGRIO_MARK
+def test_list_layers(df_points, tmpdir):
+    tempfilename = os.path.join(str(tmpdir), "dataset.gpkg")
+    df_points.to_file(tempfilename, layer="original")
+    df_points.set_geometry(df_points.buffer(1)).to_file(tempfilename, layer="buffered")
+    df_points.set_geometry(df_points.buffer(2).boundary).to_file(
+        tempfilename, layer="boundary"
+    )
+    pyogrio.write_dataframe(
+        df_points[["value1", "value2"]], tempfilename, layer="non-spatial"
+    )
+    layers = geopandas.list_layers(tempfilename)
+    expected = pd.DataFrame(
+        {
+            "name": ["original", "buffered", "boundary", "non-spatial"],
+            "geometry_type": ["Point", "Polygon", "LineString", None],
+        }
+    )
+    assert_frame_equal(layers, expected)