Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/kass507/panama-postal/llms.txt

Use this file to discover all available pages before exploring further.

An estafeta is a Panama postal office zone. Every estafeta has a human-readable name (e.g., “Estafeta de Paraiso”), a 2-character zone prefix that appears at the front of every postal code within its boundary (e.g., AC), an internal zone ID string, and a coverage area measured in square metres. Together, the estafeta prefix and the geographic body form a complete 10-character postal code. The EstafetaIndex class in panama_postal_estafeta.py loads the official boundary polygons and resolves a coordinate pair to the estafeta that contains it — no network connection required once the file is downloaded.
The estafeta.geojson.gz file is not included in the Panama Postal repository. It must be downloaded separately from the official government site before estafeta lookups will work.

Downloading the Boundary File

curl -O https://codigospostalespanama.gob.pa/estafeta.geojson.gz
Place the file in your working directory (or pass the full path to EstafetaIndex.from_file()).

How EstafetaIndex Works

Loading the file parses the GeoJSON feature collection and builds a lightweight bounding-box index over every estafeta polygon. When you call lookup(), the index:
  1. Pre-filters candidates using the bounding box — any polygon whose bbox does not contain the point is skipped immediately.
  2. Runs ray-casting (point-in-polygon) on the remaining candidates for exact containment.
  3. Resolves overlaps — if a point falls inside more than one estafeta polygon, the one with the smallest AREA value wins.
The index supports both Polygon and MultiPolygon GeoJSON geometry types, and correctly handles polygons with holes (interior rings).

Properties Returned by lookup()

idx.lookup(lat, lng) returns a dictionary of raw GeoJSON feature properties, or None if the point does not fall inside any estafeta. The most useful fields are:
PropertyDescription
ESTAF_NAMEHuman-readable estafeta name (e.g., "Estafeta de Paraiso")
VM_LEVEL2DThe 2-character estafeta prefix used in postal codes (e.g., "AC")
VM_LEVELInternal zone level string (e.g., "A6199")
AREACoverage area in m² as a string — divide by 1e6 for km²

Code Example

from panama_postal import decode
from panama_postal_estafeta import EstafetaIndex

# Load the index once; reuse it for many lookups
idx = EstafetaIndex.from_file("estafeta.geojson.gz")
print(f"Loaded {len(idx)} estafeta polygons")

# Decode a postal code to coordinates, then look up the estafeta
d = decode("ACC99-PJ42W")
info = idx.lookup(d.lat, d.lng)

if info:
    print(info["ESTAF_NAME"])                          # Estafeta de paraiso
    print(info["VM_LEVEL2D"])                          # AC
    print(info["VM_LEVEL"])                            # A6199
    print(f"{float(info['AREA']) / 1e6:.2f} km²")     # 176.95 km²
else:
    print("Point not in any estafeta")

Using encode() with Estafeta Lookup

The encode() function returns only the 8-character geographic body of a code. To build the complete 10-character code, prepend the estafeta prefix obtained from EstafetaIndex:
from panama_postal import encode
from panama_postal_estafeta import EstafetaIndex

lat, lng = 8.305509375, -79.20167109375001
body = encode(lat, lng)  # e.g. 'M2C9A-F9A33'

idx = EstafetaIndex.from_file("estafeta.geojson.gz")
info = idx.lookup(lat, lng)
prefix = info["VM_LEVEL2D"] if info else "??"

full_code = f"{prefix}{body}"
print(full_code)  # e.g. 'ACM2C9A-F9A33'

Without the Boundary File

The core library functions — decode(), encode(), and validate() — work entirely without estafeta.geojson.gz. Only the estafeta name, zone code, and area enrichment are unavailable when the file is absent. The CLI will display a reminder with the download command at startup and will continue to decode and validate codes normally.
len(idx) returns the number of estafeta polygons successfully loaded from the file. This is a quick sanity check after loading to confirm the file parsed correctly.

Build docs developers (and LLMs) love