Skip to content

Commit e58a7c6

Browse files
committed
feat: show metadata
1 parent ddcead0 commit e58a7c6

9 files changed

Lines changed: 4286 additions & 3360 deletions

File tree

.gitattributes

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
# SCM syntax highlighting & preventing 3-way merges
2+
pixi.lock merge=binary linguist-language=YAML linguist-generated=true

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -178,3 +178,6 @@ pyrightconfig.json
178178
outputs
179179

180180
config.yaml
181+
# pixi environments
182+
.pixi/*
183+
!.pixi/config.toml

.pre-commit-config.yaml

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -55,11 +55,6 @@ repos:
5555
- id: ruff
5656
- id: ruff-format
5757

58-
- repo: https://github.com/astral-sh/uv-pre-commit
59-
rev: 0.8.8
60-
hooks:
61-
- id: uv-lock
62-
6358
- repo: local
6459
hooks:
6560
- id: deptry

Dockerfile

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,16 @@
1-
FROM python:3.12-slim
1+
FROM ghcr.io/prefix-dev/pixi:latest
22

33
WORKDIR /app
44

5-
# Install uv
6-
COPY --from=ghcr.io/astral-sh/uv:latest /uv /uvx /bin/
7-
85
# Copy project files
9-
COPY pyproject.toml uv.lock ./
6+
COPY pyproject.toml pixi.lock ./
107
COPY app.py ./
118

129
# Install dependencies
13-
RUN uv sync --frozen --no-dev
10+
RUN pixi install --frozen
1411

1512
# Expose Streamlit port
1613
EXPOSE 8501
1714

1815
# Run Streamlit
19-
CMD ["uv", "run", "streamlit", "run", "app.py", "--server.port=8501", "--server.address=0.0.0.0"]
16+
CMD ["pixi", "run", "streamlit", "run", "app.py", "--server.port=8501", "--server.address=0.0.0.0"]

README.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,10 @@ A Streamlit-based web application for exploring S3-compatible storage buckets an
1717

1818
## Setup
1919

20-
Install [uv](https://docs.astral.sh/uv/getting-started/installation/), then:
20+
Install [pixi](https://pixi.sh/latest/#installation), then:
2121

2222
```bash
23-
uv sync
23+
pixi install
2424
```
2525

2626
## Configuration
@@ -51,7 +51,7 @@ export S3_EXPLORER_CONFIG=/path/to/config.yaml
5151
## Run
5252

5353
```bash
54-
uv run streamlit run app.py
54+
pixi run streamlit run app.py
5555
```
5656

5757
Then open http://localhost:8501 in your browser.

app.py

Lines changed: 37 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
from geoarrow.pyarrow.io import read_geoparquet_table
1313
from obstore.store import S3Store
1414
from omegaconf import OmegaConf
15+
from pyogrio import read_info
1516

1617

1718
def load_config():
@@ -107,6 +108,16 @@ def preview_file(bucket_cfg, bucket_name: str, path: str):
107108
"""Preview file content based on extension."""
108109
ext = pathlib.Path(path).suffix.lower()
109110

111+
http_path = f"{bucket_cfg.endpoint}/{bucket_cfg.bucket}/{path}"
112+
fs = create_fs(
113+
bucket_cfg.bucket or bucket_name,
114+
bucket_cfg.endpoint,
115+
bucket_cfg.get("access_key"),
116+
bucket_cfg.get("secret_key"),
117+
)
118+
119+
s3_path = f"s3://{bucket_name}/{path}"
120+
110121
try:
111122
if ext in (".png", ".jpg", ".jpeg", ".gif", ".webp", ".svg"):
112123
content = load_file_content(
@@ -119,40 +130,41 @@ def preview_file(bucket_cfg, bucket_name: str, path: str):
119130
st.image(content, caption=path)
120131

121132
elif ext == ".tif":
122-
full_path = f"{bucket_cfg.endpoint}/{bucket_cfg.bucket}/{path}"
123133
m = leafmap.Map()
124-
m.add_cog_layer(full_path, palette="viridis", name="Remote COG")
134+
m.add_cog_layer(http_path, palette="viridis", name="Remote COG")
125135
m.to_streamlit()
126136

127137
elif ext == ".parquet":
128-
# Use obstore fsspec filesystem
129-
fs = create_fs(
130-
bucket_cfg.bucket or bucket_name,
131-
bucket_cfg.endpoint,
132-
bucket_cfg.get("access_key"),
133-
bucket_cfg.get("secret_key"),
134-
)
138+
info = None
139+
# Read and display metadata using fiona
140+
try:
141+
info = read_info(http_path)
142+
except Exception as meta_err:
143+
st.info(f"Could not read metadata with PyOGRIO: {meta_err}")
135144

136-
full_path = f"s3://{bucket_name}/{path}"
145+
if info:
146+
with st.expander("Show file metadata (PyOGRIO)"):
147+
st.json(info)
137148

138-
# Try to read as geoparquet
139-
try:
140-
with fs.open(full_path, "rb") as f:
141-
gdf = ga.to_geopandas(read_geoparquet_table(f)).head(2000)
142-
143-
st.warning("Only the first 2000 elements are previewed")
144-
m = leafmap.Map()
145-
geom_only = gdf[[gdf.geometry.name]]
146-
m.add_gdf(geom_only, layer_name="Geometries")
147-
st.dataframe(gdf, width="stretch", height=400)
149+
if info["crs"]:
148150
try:
149-
m.to_streamlit()
151+
with fs.open(s3_path, "rb") as f:
152+
gdf = ga.to_geopandas(read_geoparquet_table(f)).head(2000)
153+
154+
st.warning("Only the first 2000 elements are previewed")
155+
m = leafmap.Map()
156+
geom_only = gdf[[gdf.geometry.name]]
157+
m.add_gdf(geom_only, layer_name="Geometries")
158+
st.dataframe(gdf, width="stretch", height=400)
159+
try:
160+
m.to_streamlit()
161+
except Exception as e:
162+
st.warning(f"Could not render map preview: {e}")
150163
except Exception as e:
151-
st.warning(f"Could not render map preview: {e}")
152-
except Exception as e:
153-
with fs.open(full_path, "rb") as f:
164+
st.warning(f"Could not render as geospatial data: {e}")
165+
else:
166+
with fs.open(s3_path, "rb") as f:
154167
table = pq.read_table(f)
155-
st.warning(f"Could not render as geospatial data: {e}")
156168
st.dataframe(table, width="stretch", height=400)
157169

158170
elif ext in (".txt", ".md", ".json", ".yaml", ".yml", ".csv", ".xml"):

0 commit comments

Comments
 (0)