Skip to content
Back to skills

Serve Vector Tiles

ASecurity

Serve geospatial data to a web map — decide GeoJSON vs vector tiles (MVT) by feature count and zoom, stand up a tile server (pg_tileserv / Martin / Tegola or MBTiles), and wire the MapLibre source + layer to consume it. Reach for this when a map is slow, you have many features, or you need a tile-serving architecture. Used by `mapping-visualization-engineer` (primary).

  • 7 stars
  • 0 votes
  • 0 copies
  • 0 views
  • Added September 23, 2026
ai-agentsrustgorails

Works with

  • cli

Security analysis

A100/100

Scanned September 23, 2026

npx -y skills add mcorbett51090/RavenClaude --skill serve-vector-tiles --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Serve Vector Tiles?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Serve Vector Tiles
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/mcorbett51090-serve-vector-tiles/badge)](https://www.skillsdirectory.com/skills/mcorbett51090-serve-vector-tiles)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: serve-vector-tiles
description: "Serve geospatial data to a web map — decide GeoJSON vs vector tiles (MVT) by feature count and zoom, stand up a tile server (pg_tileserv / Martin / Tegola or MBTiles), and wire the MapLibre source + layer to consume it. Reach for this when a map is slow, you have many features, or you need a tile-serving architecture. Used by `mapping-visualization-engineer` (primary)."
---

# Skill: serve-vector-tiles

> **Invoked by:** `mapping-visualization-engineer` (primary). Routes the source schema/index questions to `geospatial-data-engineer`.
>
> **When to invoke:** "how do I serve N features?"; "my map is slow / blank"; "vector tiles or GeoJSON?"; standing up any tile-serving layer.
>
> **Output:** the format decision (GeoJSON vs MVT, by size/zoom) + a tile-server recipe + the MapLibre source/layer style to consume it, in EPSG:3857.

## Procedure

1. **Make the format decision first.** Count features and consider zoom interactivity:
   - **≲ a few hundred static features, no per-zoom simplification** → ship **GeoJSON** directly to MapLibre (simple, no server).
   - **thousands → millions, or zoom-dependent, or restyleable** → **vector tiles (MVT)** from a tile server.
   - **true imagery / heavy cartography** → **raster tiles** (XYZ/WMTS).
2. **Pick the tile server.** PostGIS-backed dynamic tiles → **pg_tileserv** (zero-config, table-driven) or **Martin** (fast, Rust, config-driven); pre-generated tiles → **Tegola** or an **MBTiles** file served by a static tile host.
3. **Guarantee the projection.** Web tiles are **EPSG:3857**. The server runs `ST_AsMVT(ST_AsMVTGeom(ST_Transform(geom, 3857), tile_bbox))` — reproject from the source SRID and clip to the tile. A mismatch = a blank or mis-placed map.
4. **Simplify per zoom.** `ST_SimplifyPreserveTopology` or tile-level `ST_AsMVTGeom` clipping keeps tiles small; don't ship z18 detail at z3.
5. **Wire the MapLibre source + layer.** Add a `vector` source pointing at the `{z}/{x}/{y}` tile URL, then a layer naming the `source-layer`, with zoom-dependent paint.

## Worked example

> User: "I have 400k parcels in PostGIS and a MapLibre map that hangs. How do I serve them?"

- 400k features → **vector tiles**, not GeoJSON. Use pg_tileserv over the indexed source table; reproject to 3857 in the tile query.

```json
// MapLibre style fragment — consume the vector-tile source
{
  "sources": {
    "parcels": {
      "type": "vector",
      "tiles": ["https://tiles.example.com/public.parcel/{z}/{x}/{y}.pbf"],
      "minzoom": 0, "maxzoom": 16
    }
  },
  "layers": [
    {
      "id": "parcel-fill",
      "type": "fill",
      "source": "parcels",
      "source-layer": "public.parcel",
      "paint": {
        "fill-opacity": ["interpolate", ["linear"], ["zoom"], 10, 0, 13, 0.4]
      }
    }
  ]
}
```

The pg_tileserv source query (run by the server) must `ST_Transform(geom, 3857)` and rely on the table's GiST index for the bbox filter — route that schema/index work to [`../../agents/geospatial-data-engineer.md`](../../agents/geospatial-data-engineer.md).

## Guardrails
- Never ship a multi-megabyte GeoJSON to the browser when feature count says vector tiles — that is the #1 slow-map cause.
- Tiles are 3857: a source in 4326 must be `ST_Transform`ed in the tile query, or the map is blank/mis-placed.
- A `source-layer` name mismatch produces an empty layer with no error — check it first when a styled layer doesn't render. See [`../../best-practices/vector-tiles-over-geojson-at-scale.md`](../../best-practices/vector-tiles-over-geojson-at-scale.md) and [`../../templates/tile-serving-architecture.md`](../../templates/tile-serving-architecture.md).

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…