---
name: reserve-maps
description: Makes honest maps of a protected area, reserve or conservation site from real boundary and coastline data, with sources, licence terms and "not for navigation" shown on the map. Use when someone asks for a map of their reserve, park or site for a report, funder pack, briefing or slide deck, asks for a simple web map of it, or asks where to get boundary or coastline data (WDPA, OpenStreetMap, national data) and whether they may use it.
license: MIT
compatibility: Needs Python 3.9 or later and internet access to fetch data. PNG rendering uses resvg-py through uv or pip.
---

# Reserve maps

A reserve map earns trust when every line on it comes from a dataset a reader can name and check. This skill fetches real geometry, draws a clean map with its sources printed on it, and checks the rendered picture before anyone sees it.

Paths below are relative to this skill's folder. The scripts use only the Python standard library, so `python3 scripts/<name>.py` runs anywhere Python does. Run `--help` on any script for its options.

## Ground rules

- **Real geometry only.** Every boundary, coastline and island comes from a dataset you fetched or the user supplied. Coordinates you type from memory, estimate from a picture or copy from a description are a sketch. When a layer cannot be fetched, the map goes without it and the handover says so. Draw a sketch only when the user asks for one, and label the map "Illustrative only".
- **Sources on the map.** The image itself carries each dataset's attribution, the retrieval date, the projection and "Not for navigation". A map copied out of the report keeps its credits.
- **Licence before publishing.** Match each dataset's terms to the use. WDPA geometry is for non-commercial use only and may not be redistributed. A funder pack can count as revenue generation under those terms. [sources-and-licences.md](references/sources-and-licences.md) has the terms for every source.
- **Look before handing over.** Render to PNG and look at the image. A script that exits cleanly proves nothing about the picture.

## Make a report map

This is the default: a static map for a document, deck or briefing.

1. **Settle the use.** Establish what the map is for (internal report, funder pack, public website, printed sign), where it will appear and at what printed width. Default to a 170 mm wide figure in an A4 report, delivered as SVG and PNG. Done when you can say whether the use is commercial, fundraising or public, because that decides which boundary source you may use. An annual report that also goes to donors counts as possible fundraising: flag the WDPA condition in the handover.

2. **Find the boundary.** Run `python3 scripts/fetch_geometry.py search "<name>"`. It lists WDPA records, then OpenStreetMap matches for your text and for each WDPA name. When nothing matches, try one distinctive word, then the local-language name. Pick the source in this order: a file from the managing authority, then WDPA (`wdpa <site_id> park.geojson`), then OpenStreetMap (`osm-area R<id> park.geojson`). The `wdpa` command prints the managing authority WDPA records; name it in the handover as the source of a better file. Fetch two sources when you can and compare them. A boundary with only a handful of vertices is either straight lines between legal coordinates (common for marine parks) or a sketch: when both sources agree it is the former. Done when `park.geojson` exists and you know which source it came from and why you chose it.

3. **Frame the map.** Run `python3 scripts/draw_map.py extent park.geojson`. It prints a `west,south,east,north` box around the boundary with 15% padding, shaped for a page. Pass `--aspect 16:9` for slides. Whenever you change the box later, fetch coastline and places again for the new box: land is only correct inside the box it was built for, and `draw_map.py` refuses a larger extent.

4. **Fetch context for exactly that box.**
   ```sh
   python3 scripts/fetch_geometry.py coastline <box> land.geojson
   python3 scripts/fetch_geometry.py places <box> places.geojson
   python3 scripts/fetch_geometry.py countries countries.geojson
   ```
   `coastline` builds land polygons from the OpenStreetMap coastline and warns when a piece of coast is broken. `places` lists island, town and village names so you can choose which to show. `countries` feeds the locator inset. Inland sites have no coastline: skip it and add rivers, roads or neighbouring areas as `line` or `area` layers from OSM or national data.

5. **Draw.** Write `map.json` and run `python3 scripts/draw_map.py draw map.json map.svg`. The minimum:
   ```json
   {
     "title": "Mafia Island Marine Park",
     "subtitle": "Pwani Region, Tanzania",
     "extent": [39.4648, -8.1182, 39.9776, -7.7471],
     "layers": [
       {"file": "land.geojson", "style": "land"},
       {"file": "park.geojson", "style": "boundary", "legend": "Marine park boundary (WDPA)"},
       {"file": "places.geojson", "style": "places", "exclude": ["<a name you chose to leave out>"]}
     ],
     "water_labels": [{"text": "INDIAN OCEAN", "lon": 39.93, "lat": -7.95}],
     "locator": {"file": "countries.geojson", "highlight": "TZA"}
   }
   ```
   Every field, including extra layers, colours and print width, is in [draw-map-spec.md](references/draw-map-spec.md). Read the script's summary: it names dropped labels, near-duplicate place names, unsourced layers, holes in boundary polygons, and the printed height of the figure.

6. **Render and look.** Run `uv run --with resvg-py python3 scripts/render_png.py map.svg map.png --width 2008` (300 dpi at 170 mm; see [cartography.md](references/cartography.md) for other sizes), then open `map.png` and view it. Without uv, run `pip install resvg-py` first. Work through the checklist below and fix what fails. Done when every item passes.

7. **Hand over.** Deliver the SVG and PNG, a one-line caption with the same sources, alt text, and a short note naming the boundary source, its licence conditions for this use, and anything missing or uncertain. [cartography.md](references/cartography.md) covers file formats, resolution and placing the map in Word, Google Docs, slides, HTML or Markdown.

## Rendered map checklist

Check each item against the PNG you looked at:

- The boundary sits where the reserve is: islands, coast and towns line up with a map you trust, and nothing is mirrored or shifted (a latitude/longitude swap moves the whole map).
- Land, sea and the boundary are distinct at a glance, and the boundary is the strongest line on the map.
- Holes in the boundary polygon are real. When the script reports holes, find out what they exclude (often islands or enclaves) and say so in the caption.
- Where two sources trace the same coast they will differ slightly. The gap is honest; mention it when it is wider than a line.
- Every label is legible at the printed width, none collides with another, and duplicates or spelling variants are gone (the script drops near-duplicates and names them).
- Title, legend, scale bar, north arrow, source lines, retrieval dates, projection and "Not for navigation" are all visible.
- WDPA maps show the release month and year from the citation.
- Nothing important is hidden behind the locator, legend or frame edge.
- The figure fits its page: the script prints the printed height and flags anything over 230 mm.
- In greyscale, land, sea and the boundary still separate.

## When the situation differs

- **The sandbox blocks the network.** The Claude app's code execution, and some locked-down machines, cannot reach these data hosts. The fetch script fails with a message saying so. Ask the user to download the files instead (links in [sources-and-licences.md](references/sources-and-licences.md)), then record their provenance with `fetch_geometry.py wrap`.
- **The user has their own boundary file** (shapefile, KML, GeoPackage): it usually beats every public source. Convert it and record where it came from, as shown in [sources-and-licences.md](references/sources-and-licences.md).
- **They want a web map** they can pan and zoom: [web-maps.md](references/web-maps.md). WDPA geometry needs special handling there.
- **Satellite imagery, habitat rasters, large datasets or a repeatable pipeline**: [rasters-and-pipelines.md](references/rasters-and-pipelines.md).
- **Custom styling, several zones, a different projection or a map drawn with other tools** (QGIS, geopandas): [cartography.md](references/cartography.md).
