Skip to main content

Python SDK for the Township Canada API — convert Canadian legal land descriptions (DLS, NTS, Geographic Townships) to GPS coordinates and back.

Project description

Township Canada Python SDK

PyPI License: MIT

Convert Canadian legal land descriptions (DLS, NTS, Geographic Townships) to GPS coordinates and back. Covers Alberta, Saskatchewan, Manitoba, British Columbia, and Ontario.

Documentation · GitHub · PyPI

Installation

pip install townshipcanada

Quick Start

import os

from townshipcanada import TownshipCanada

tc = TownshipCanada(os.environ["TOWNSHIP_API_KEY"])

# DLS (Dominion Land Survey) — Alberta, Saskatchewan, Manitoba
result = tc.search("NW-36-42-3-W5")
print(f"{result.latitude}, {result.longitude}")
# 52.123456, -114.654321

# NTS (National Topographic System) — British Columbia
result = tc.search("A-2-F/93-P-8")

# Geographic Townships — Ontario
result = tc.search("Lot 2 Con 4 Osprey")

Get an API key at townshipcanada.com/api.

Examples

1. Oil & Gas: Convert Well Locations to GPS

from townshipcanada import TownshipCanada

tc = TownshipCanada("your_api_key")

well_locations = [
    "NW-36-42-3-W5",
    "SE-1-50-10-W4",
    "10-14-42-4-W4",
]

# Batch convert all at once (up to 100 per request, auto-chunks larger arrays)
result = tc.batch_search(well_locations)

for item in result.results:
    print(
        f"{item.legal_location} -> "
        f"{item.latitude:.6f}, {item.longitude:.6f} "
        f"({item.province})"
    )

2. GIS Pipeline: Reverse Geocode Field Coordinates

from townshipcanada import TownshipCanada

tc = TownshipCanada("your_api_key")

# GPS coordinates from a field survey
field_points = [
    (-114.648933, 52.454928),
    (-110.456789, 50.321654),
    (-106.123456, 52.789012),
]

# Batch reverse geocode to legal land descriptions
result = tc.batch_reverse(field_points, unit="Quarter Section")

for item in result.results:
    print(item.legal_location)

3. Real Estate: Look Up a Single Parcel with GeoPandas

import geopandas as gpd
from shapely.geometry import shape

from townshipcanada import TownshipCanada

tc = TownshipCanada("your_api_key")

result = tc.search("NW-36-42-3-W5")

# Convert the grid boundary to a Shapely geometry
if result.boundary:
    geometry = shape(result.boundary.model_dump())

    gdf = gpd.GeoDataFrame(
        [{"legal_location": result.legal_location, "province": result.province}],
        geometry=[geometry],
        crs="EPSG:4326",
    )

    print(gdf)
    # gdf.to_file("parcel.geojson", driver="GeoJSON")

Async Support

import asyncio

from townshipcanada import AsyncTownshipCanada


async def main():
    async with AsyncTownshipCanada("your_api_key") as tc:
        result = await tc.search("NW-36-42-3-W5")
        print(result.latitude, result.longitude)


asyncio.run(main())

CLI

The SDK includes a command-line tool:

# Set your API key
export TOWNSHIP_API_KEY="your_api_key"

# Convert a legal land description
township convert "NW-36-42-3-W5"
# 52.123456, -114.654321
#   Location:  NW-36-42-3-W5
#   Province:  Alberta
#   System:    DLS
#   Unit:      Quarter Section

# Reverse geocode
township reverse -- -114.654321 52.123456

# JSON output
township convert "NW-36-42-3-W5" --json

API Reference

TownshipCanada(api_key, *, base_url=..., timeout=30.0)

Method Description
search(location) Convert legal land description to GPS
reverse(longitude, latitude, *, survey_system=None, unit=None) Find legal land description at GPS
autocomplete(query, *, limit=None, proximity=None) Get search suggestions
batch_search(locations, *, chunk_size=100) Batch convert up to 100+ descriptions
batch_reverse(coordinates, *, survey_system=None, unit=None, chunk_size=100) Batch reverse geocode up to 100+ points
boundary(location) Get boundary polygon only
raw(location) Get raw GeoJSON FeatureCollection

All methods are also available on AsyncTownshipCanada as async/await.

Return Types

SearchResult — returned by search(), reverse()

Field Type Description
legal_location str Normalized legal description
latitude float Centroid latitude
longitude float Centroid longitude
province str Province name
survey_system str DLS, NTS, or GTS
unit str Resolution unit
boundary Polygon | MultiPolygon | None Grid boundary polygon
raw List[Feature] Raw GeoJSON features

BatchResult — returned by batch_search(), batch_reverse()

Field Type Description
results List[SearchResult] Successfully converted
total int Total items submitted
success int Successful conversions
failed int Failed conversions

AutocompleteSuggestion — returned by autocomplete()

Field Type Description
legal_location str Full legal land description
latitude float Centroid latitude
longitude float Centroid longitude
survey_system str Survey system
unit str Resolution unit

Exceptions

Exception HTTP Status Description
ValidationError 400 Invalid request parameters
AuthenticationError 401 Missing or invalid API key
NotFoundError 404 No results found
RateLimitError 429 Rate limit exceeded
PayloadTooLargeError 413 Batch exceeds 100 items
ServerError 5xx Server-side error
from townshipcanada import (
    TownshipCanada,
    AuthenticationError,
    NotFoundError,
    RateLimitError,
    ValidationError,
)

tc = TownshipCanada("your_api_key")

try:
    result = tc.search("INVALID")
except NotFoundError:
    print("Location not found")
except AuthenticationError:
    print("Check your API key")
except RateLimitError as e:
    print(f"Rate limited — retry after {e.retry_after}s")

Supported Survey Systems

System Provinces Format Examples
DLS (Dominion Land Survey) AB, SK, MB NW-36-42-3-W5, 10-36-42-3-W5, 36-42-3-W5
NTS (National Topographic System) BC A-2-F/93-P-8, 2-F/93-P-8
GTS (Geographic Townships) ON Lot 2 Con 4 Osprey

Requirements

  • Python 3.9+
  • Dependencies: httpx, pydantic

License

MIT — Maps & Apps Inc.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

townshipcanada-1.0.1.tar.gz (11.9 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

townshipcanada-1.0.1-py3-none-any.whl (12.2 kB view details)

Uploaded Python 3

File details

Details for the file townshipcanada-1.0.1.tar.gz.

File metadata

  • Download URL: townshipcanada-1.0.1.tar.gz
  • Upload date:
  • Size: 11.9 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: Hatch/1.16.5 cpython/3.12.12 HTTPX/0.28.1

File hashes

Hashes for townshipcanada-1.0.1.tar.gz
Algorithm Hash digest
SHA256 c81ce7919b0ef847005ec753d6cbc293df7e98783346a6faf8235d0eab3457f9
MD5 510f001f3e55b25c4ea2f82ea20fd046
BLAKE2b-256 132ec3150bd70f379d2ff3929192041f2e76931b555ba2bd64108b6f257a7409

See more details on using hashes here.

File details

Details for the file townshipcanada-1.0.1-py3-none-any.whl.

File metadata

  • Download URL: townshipcanada-1.0.1-py3-none-any.whl
  • Upload date:
  • Size: 12.2 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: Hatch/1.16.5 cpython/3.12.12 HTTPX/0.28.1

File hashes

Hashes for townshipcanada-1.0.1-py3-none-any.whl
Algorithm Hash digest
SHA256 166dc7b6fab4677bf1f0d82cf6f253ba2d810e6454f11bb2df2acc2d958423fd
MD5 73a801c219eb38e27cbad638e0d992d4
BLAKE2b-256 fc85aad6000ebbd2b63558d5eb7a4d1b738da6a98fb28cf827bfe9daac5568cc

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page