portolan-thumbnails
Generate framed, checked thumbnails from Portolan collections using chiitiler (MapLibre GL Native).
Skill by portolan-sdi
Install
Works with Claude Code, Claude apps, Codex, Cursor, Gemini CLI, GitHub Copilot, VS Code, OpenCode, Goose, Windsurf. Read the repository before you give any tool access to your data.
npx agentmarket install skill/portolan-sdi-portolan-thumbnails
Asks which of your tools to add it to (Claude Code, Codex, Cursor, VS Code, Windsurf, Gemini CLI, Claude Desktop) and shows the change first. See https://agentmart.dev/cli.
Get the weekly diff
The weekly diff: one email a week with new agent tools, notable updates and grade changes, plus security alerts for tools you follow. Double opt-in, one-click unsubscribe.
About
Use cases: backend
Reviews
Write a reviewNo reviews yet. Installed it? Reviews come from verified installers only. How reviews work
Permissions
What this skill can do and what it asks for, from its SKILL.md and code. Checked Oct 25, 2026.
- Tools
- No tools found
- Secrets
- None asked for
- Install scripts
- None found
- Runs as
- Python package
Network hosts it talks to 4
codeload.github.com· found in install commandgithub.com· found in codeservices.arcgisonline.com· found in codetile.openstreetmap.org· found in code
Files it touches
~/.claude/skills/portolan-thumbnails
What's inside
- Skill name
portolan-thumbnails- Description
- Generate framed, checked thumbnails from Portolan collections using chiitiler (MapLibre GL Native). Renders the collection's default style server-side over the PMTiles the collection links to, with an optional basemap, frames every bbox to the browser card's 3:2 shape, accepts an image only after an automated blank probe and a visual review, then refreshes file:size and file:checksum with portolan check and its fix flag. Requires Node.js 24.12 or newer.
- License
- Apache-2.0
- Instructions
- 389 lines in SKILL.md · view on GitHub
Before installing
- Ships executable scripts
- Instructions call the network
Files (7)
- SKILL.md
- buildstyle.py · scripts/
- frame.py · scripts/
- pick_basemap.sh · scripts/
- read_collection.py · scripts/
- render_one.sh · scripts/
- start_server.sh · scripts/
SKILL.md (first lines)
<!-- drift: depends-on: portolan-cli, portolan-spec -->
# Framed Thumbnails with Chiitiler
Render a collection's default style server-side with
[chiitiler](https://github.com/Kanahiro/chiitiler), frame it to the shape of the
browser card, and check the result before you publish it. The renderer is the
easy half.
Most bad thumbnails come from the bbox.
Use this for any collection whose thumbnail people will look at. The thumbnails
that `portolan add` and `portolan check --fix` draw are the baseline. This replaces
them with an image that matches the portal. The scripts live in `scripts/` beside
this file. Set `SKILL` to this skill's directory once per session.
## Requirements
- Node.js 24.12 or newer, and npm. chiitiler imports `node:sqlite`, so an older
Node fails at startup with `ERR_UNKNOWN_BUILTIN_MODULE`.
- Git, to clone chiitiler.
- A local checkout of the collection with a `rel: pmtiles` link and a style asset.
- An agent that can view images. Step 5 is not optional.
- DuckDB, optional. Needed only to find a dense subregion in a large collection.
The scripts run on Linux and macOS. They use `nohup` in place of `setsid`, and
fall back from `stat -c%s` and `sha256sum` to `stat -f%z` and `shasum -a 256`.
## Why Framing Is the Whole Job
chiitiler's `/clip` endpoint takes `bbox`, `size`, `quality`, and a style. The
aspect ratio and the zoom both fall out of the bbox, so every framing decision is
a decision about which bbox to send. The raw extent produces tall rectangles in
wide cards, dense layers rendered so far out that tile thinning shows as holes,
and a row of cards that all look alike. Rewriting the bbox reframes the map. It
keeps the aspect ratio of every feature.
`specs/best-practices/conversion-defaults.md`
describes the CLI's own thumbnail as guidance, not conformance.
## Step 1: Read the collection
`read_collection.py` reads the signals from `collection.json` by role and by link
relation, never by file name. Styles are assets with the `style` role, and the
default one also carries `default` (PORTO-CORE-069, PORTO-CORE-070). The PMTiles
is a `rel: pmtiles` link with `pmtiles:layers` (PORTO-FMT-011).
```bash
python3 "$SKILL/scripts/read_collection.py" publico_arbolado/
# {"bbox": "-60.63,-33.92,-60.51,-33.86", "style": ".../styles/default.json",
# "pmtiles": ".../publico_arbolado.pmtiles", "pmtiles_layers": ["publico_arbolado"],
# "thumbnail": ".../publico_arbolado.thumb.jpg", "thumbnail_type": "image/jpeg",
# "geometry_type": "Point", "feature_count": 43525}
```
`bbox` is the starting frame. `thumbnail` and `thumbnail_type` say where to write
and in which format. `pmtiles_layers` is ground truth for `source-layer`.
`feature_count` is the strongest strategy signal, and points and lines need more
zoom than polygons. portolan-cli writes those last two as `geoparquet:*`
properties. The spec defines none of them, and it defines neither
`pmtiles:max_zoom` nor `pmtiles:center`. A catalog from another tool lacks them.
Read the zoom range and
centre from the PMTiles header instead, which always works.
```bash
python3 "$SKILL/scripts/frame.py" \
--pmtiles publico_arbolado/publico_arbolado.pmtiles
# {"min_zoom": 0, "max_zoom": 13, "center": [-60.578613, -33.888655, 13],
# "bounds": [-60.638266, -33.923181, -60.512274, -33.860783]}
```
Treat `center` as a hint. tippecanoe sometimes writes a bbox corner rather than a
dense cluster. Confirm it with a feature count before you build a window around it.
## Step 2: Choose a strategy
**A, full extent.** Frame the whole bbox. Right for boundaries, districts, wards,
neighborhoods, watersheds, city limits, and anything with a small feature count.
**B, zoomed window.** A 3:2 window at a chosen zoom, centred on a dense cluster.
Right for parcels, buildings, addresses, service requests, permits, and trees.
These are defaults. Override them when the data says otherwise.
| Signal | Default |
|---|---|
| Polygons, `feature_count` <= 50 | A |
| Name matches boundary, border, limit, district, ward, precinct, zone, tract, county, city, watershed, region, and geometry is polygons | A |
| Any geometry, `feature_count` >= 5000 | B |
| Points or lines, `feature_count` >= 1000 | B |
| Anything else | A when `fill` >= 0.4, otherwise B |
| `aspect` outside 2.2:1 after capping | Decide explicitly and record why |
`fill` and `aspect` come from Step 3. The last two rows mean you run `frame.py`
first and then reconsider.
## Step 3: Compute the bbox
`frame.py` reshapes a bbox to 3:2 in Web Mercator and reports `fill`, `aspect`,
and the effective zoom.
| Knob | Default | Why |
|---|---|---|
| `TARGET_ASPECT` | 1.5 | The browser card is 350 to about 700 px wide and 250 px tall |
| `MARGIN` | 0.05 | Fractional per axis, so the aspect is unchanged |
| `MAX_CONTEXT` | 2.5 | The short axis grows to at most 2.5x the data span, so data stays at 40% of the frame or more |
| `FRAME_ASPECT_LIMIT` | 2.2 | A capped frame still worse than 2.2:1 means full-extent framing failed |
| `--size` | 1024 | Twice the widest realistic card |
### Strategy A
```bash
BBOX=$(python3 "$SKILL/scripts/read_collection.py" publico_barrios/ \
| python3 -c "import json,sys; print(json.load(sys.stdin)['bbox'])")
python3 "$SKILL/scripts/frame.py" --bbox "$BBOX" --size 1024
# {"bbox": "-60.651220,-33.935211,-60.495382,-33.848971", "aspect": 1.5,
# "zoom": 13.17, "span_m": 17347.9, "warnings": [], "fill": 0.957}
```
Read `fill` and `aspect` before you render. A `fill` near 1 means the data nearly
fills the frame. A `fill` of exactly 0.4 means `MAX_CONTEXT` capped the growth andFrom portolan-sdi/portolan-skills, Apache-2.0.
More from portolan-sdi/portolan-skills
- 0.637score
enrich-metadata
portolan-sdi
Fill a collection's metadata.yaml from the open data portal record that lists the same data, when the data came from a source with no metadata of its own…
SkillAFreshness: A★ 11Claude Code +9 - 0.637score
catalog-grader
portolan-sdi
Grade a published Portolan catalog from A+ to F and explain each finding with evidence.
SkillAFreshness: A★ 11Claude Code +9 - 0.637score
portolan-bootstrap
portolan-sdi
Build a complete, well-documented Portolan catalog from a data source.
SkillAFreshness: A★ 11Claude Code +9 - 0.632score
portolan-cli
portolan-sdi
Use when publishing, managing, or converting cloud-native geospatial data catalogs with the Portolan CLI.
SkillAFreshness: A★ 11Claude Code +9 - 0.632score
reading-portolan
portolan-sdi
Use when exploring, querying, analyzing, or visualizing data from a Portolan catalog (STAC-based cloud-native geospatial data).
SkillAFreshness: A★ 11Claude Code +9 - 0.632score
portolan-migrate
portolan-sdi
Bring an existing non-compliant catalog or published dataset into Portolan compliance without rebuilding it.
SkillAFreshness: A★ 11Claude Code +9 - 0.632score
customize-portolan-browser
portolan-sdi
Adapt a portolan-browser checkout into a publisher-branded interface for one existing Portolan catalog.
SkillAFreshness: A★ 11Claude Code +9 - 0.632score
git-backed-catalog
portolan-sdi
Create, maintain, or contribute to a Portolan catalog whose metadata is stored in a git repository, with CI that validates each change.
SkillAFreshness: A★ 11Claude Code +9 - 0.626score
report-catalog-issue
portolan-sdi
Report a problem with a catalog registered in the Portolan registry by opening a catalog feedback issue against it.
SkillAFreshness: A★ 11Claude Code +9 - 0.626score
sourcecoop
portolan-sdi
Upload geospatial data to Source Cooperative with proper metadata and READMEs using Portolan CLI.
SkillAFreshness: A★ 11Claude Code +9 - 0.621score
register-catalog
portolan-sdi
Submit a Portolan catalog to the registry by following the registry README.
SkillAFreshness: A★ 11Claude Code +9 - 0.618score
good
portolan-sdi
Fixture skill with no defects.
SkillAFreshness: A★ 11Claude Code +9
Signals
- Stars
- 11
- Forks
- 3
- Open issues
- 5
- Last commit (this folder)
- Oct 6, 2026 (22 days ago)
- Last release
- Oct 6, 2026 (22 days ago)
From GitHub (fetched Oct 24, 2026).
Details
- License
- Apache-2.0
- Repository
- github.com/portolan-sdi/portolan-skills
- First seen
- Oct 24, 2026
- Last checked
- Oct 24, 2026
- Source
- GitHubgithub-graphql
More from portolan-sdi
- 0.637score
enrich-metadata
portolan-sdi
Fill a collection's metadata.yaml from the open data portal record that lists the same data, when the data came from a source with no metadata of its own…
SkillAFreshness: A★ 11Claude Code +9 - 0.637score
portolan-bootstrap
portolan-sdi
Build a complete, well-documented Portolan catalog from a data source.
SkillAFreshness: A★ 11Claude Code +9 - 0.637score
catalog-grader
portolan-sdi
Grade a published Portolan catalog from A+ to F and explain each finding with evidence.
SkillAFreshness: A★ 11Claude Code +9 - 0.632score
portolan-cli
portolan-sdi
Use when publishing, managing, or converting cloud-native geospatial data catalogs with the Portolan CLI.
SkillAFreshness: A★ 11Claude Code +9 - 0.632score
git-backed-catalog
portolan-sdi
Create, maintain, or contribute to a Portolan catalog whose metadata is stored in a git repository, with CI that validates each change.
SkillAFreshness: A★ 11Claude Code +9 - 0.632score
customize-portolan-browser
portolan-sdi
Adapt a portolan-browser checkout into a publisher-branded interface for one existing Portolan catalog.
SkillAFreshness: A★ 11Claude Code +9
Cite this page
portolan-thumbnails skill. Agent Mart. https://agentmart.dev/skills/portolan-sdi-portolan-thumbnails (last updated 2026-10-24)