marimo/skills/osm-data-layer/SKILL.md
OpenStreetMap data pipeline tooling: tippecanoe (GeoJSON → MBTiles/PMTiles, built from source), osmium-tool, gdal/ogr2ogr, jq, martin (Rust musl static binary on port 3000), pmtiles CLI. Martin reads tiles from ${HOME}/workspace/tiles/pmtiles/. Use when working with the osm-data layer, tippecanoe build steps, the martin tile server config, the martin "Underlying data source was modified" cache issue + DAG-completion supervisord-restart pattern, or the vector-tiles-only output that requires MapLibre GL JS clients (NOT folium TileLayer).
npx skillsauth add overthinkos/overthink-plugins osm-data-layerInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Bundles every CLI needed to ingest, transform, and serve OSM data in the marimo-ml image. Martin (Rust) is the long-running tile server at port 3000 (host-mapped to 23000); everything else is invoked ad-hoc from DAGs / shell.
| Property | Value |
|----------|-------|
| Dependencies | supervisord |
| Distros | fedora (sole; tippecanoe needs gcc-c++/sqlite-devel/zlib-devel for source build) |
| Ports | 3000 (martin HTTP, host-mapped to 23000) |
| Service | martin (supervisord, restart: always) |
| Tile dir | ~/workspace/tiles/pmtiles/ |
| Distros packages | osmium-tool, gdal, jq + tippecanoe build deps (git, make, gcc-c++, sqlite-devel, zlib-devel) |
| Tool | Source | Purpose |
|---|---|---|
| tippecanoe | Built from felt/tippecanoe source | GeoJSON → MBTiles/PMTiles |
| osmium-tool | Fedora pkg osmium-tool | OSM PBF inspection / filtering / transformation |
| ogr2ogr / ogrinfo | Fedora pkg gdal | Format conversion (e.g. GeoParquet → GeoJSON for tippecanoe) |
| jq | Fedora pkg jq | JSON manipulation (also for inspecting martin's /catalog) |
| martin | GitHub release musl binary | Rust tile server |
| pmtiles | (separate install) | PMTiles inspection |
Tippecanoe is built from source because no Fedora package exists and
upstream ships only source. Build is small (~200 MB clone, <1 min
make -j); cleanup keeps the image lean.
Martin is the musl static binary, NOT the gnu build — gnu
hard-depends on libicuuc.so.74 / libicui18n.so.74 which Fedora
43 doesn't ship (it has libicu 77). musl is self-contained.
service:
- name: martin
exec: /usr/local/bin/martin-wrapper.sh
restart: always
working_directory: /home/user
The wrapper:
#!/usr/bin/env bash
set -euo pipefail
DIR="${HOME}/workspace/tiles/pmtiles"
mkdir -p "$DIR"
exec /usr/local/bin/martin --listen-addresses 0.0.0.0:3000 "$DIR"
The mkdir -p ensures martin doesn't error/warn when the dir
doesn't exist yet (pre-DAG-run state on a fresh deploy). Martin
auto-discovers tile files in the directory and serves each as a
named source.
When martin starts, it caches the mtime of every pmtiles file in its watched directory. If the file is rewritten LATER (e.g. by a DAG re-run), martin returns:
HTTP/1.1 500 Internal Server Error
Underlying data source was modified
for some tile fetches and HTTP 204 No Content for others. All
tile fetches break until martin restarts and re-reads the file.
Martin has NO --watch flag (verified via martin --help). The
--cache-expiry and --cache-idle-timeout flags control the tile
cache, not the file-mtime cache.
Fix in the OSM DAG: add a reload_martin task as the final
DAG step:
@task
def reload_martin(pmtiles_path: str) -> str:
import subprocess
subprocess.run(["supervisorctl", "restart", "martin"], check=True)
return pmtiles_path
uid 1000 has supervisorctl access in this image (the supervisord.sock
is readable by the container user). The DAG triggers martin restart
after writing each new pmtiles, so martin always serves the freshest
tiles after any DAG run.
Tippecanoe produces Mapbox Vector Tile (MVT/PBF) format. Martin
serves them with Content-Type: application/x-protobuf. Folium's
TileLayer is RASTER-only — it tries to render the PBF as PNG/JPG,
fails silently → grey map.
The canonical client for vector tiles is MapLibre GL JS.
/ov-marimo:notebook-osm cell 7 uses mo.iframe with embedded
MapLibre HTML pointing at martin's TileJSON URL
(http://localhost:23000/monaco) as a vector source, with separate
polygon/line/circle layers filtered by geometry-type.
/ov-marimo:maputnik-layer is a visual editor for these styles —
useful for iterating on map appearance without notebook rebuilds.
Martin auto-discovers files in the watched directory and exposes:
| URL | Purpose |
|---|---|
| /catalog | JSON listing of all available sources |
| /<source> | TileJSON for a source (use as MapLibre style.sources[…].url) |
| /<source>/{z}/{x}/{y} | Vector tile fetch for a coord |
| /health | Liveness probe |
For monaco.pmtiles → source name monaco → tiles at
/monaco/{z}/{x}/{y}. Martin reflects the request Origin in
access-control-allow-origin so cross-port (22718 → 23000) XHR from
marimo works without proxy tricks.
Build-scope:
tippecanoe-installed, osmium-installed, gdal-installed,
jq-installed, martin-installed, pmtiles-installed — --version
exit 0martin-wrapper-installed — /usr/local/bin/martin-wrapper.sh
exists with mode 0755Deploy-scope:
martin-running — supervisord program is RUNNINGmartin-port-reachable — TCP 3000 reachablemartin-http-catalog — GET /catalog returns 200martin-tiles-dir-runtime — ~/workspace/tiles/pmtiles/ existsThe catalog probe passes pre-DAG (with empty source list) and post-DAG (with the monaco source listed). Tile-content probes are NOT included — they'd false-fail on cold deploys before any DAG ran.
/ov-marimo:marimo-ml — image composing this layer/ov-marimo:notebook-osm — DAG that produces pmtiles + MapLibre cell consuming them/ov-marimo:maputnik-layer — visual style editor for the same vector tiles/ov-infrastructure:supervisord — service runtimetools
Use when authoring or modifying a charly PLUGIN — a candy with a `plugin:` block that contributes Providers (verbs/kinds/deploy-targets/steps/builders/commands), its own CUE schema, builtin (compiled-in) or external (out-of-tree git repo). Covers the unified Provider model, the per-plugin CUE-schema contract (single source → Go params for dev + schema-over-Describe RPC for runtime), the SDK, and the loader.
tools
The CUE data-validation / configuration CLI (cue), pinned to v0.16.1. Use when working with the cue candy, installing the cue binary into a box or onto a target:local dev host, or running the offline schema-vendoring pipeline that feeds charly's egress validation.
tools
CUE EGRESS validation — validating (and, where it adds value, generating) the config files charly WRITES to a system BEFORE the bytes hit disk. MUST be invoked before working on charly/egress.go, the vendored schemas under candy/plugin-egress/egress-schemas/vendor/, the ValidateEgress / registerVendoredEgressKind path, the offline `task cue:vendor` pipeline, or adding an egress schema for any written artifact (cloud-init, k8s manifests, traefik routes, runtime config, install ledger, systemd/quadlet units, ssh_config, libvirt XML).
tools
Kubernetes cluster-probe declarative check verb — the `kube:` check verb (nodes, pods, ingress, storage class, addon health, apply/delete, and arbitrary resource GETs) served out-of-process by the candy/plugin-kube plugin (vendored client-go; no external kubectl required).