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

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

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.0.tar.gz (11.4 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.0-py3-none-any.whl (12.1 kB view details)

Uploaded Python 3

File details

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

File metadata

  • Download URL: townshipcanada-1.0.0.tar.gz
  • Upload date:
  • Size: 11.4 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.14.3

File hashes

Hashes for townshipcanada-1.0.0.tar.gz
Algorithm Hash digest
SHA256 32efab63c8e8dd2a5c859a72888bdbe528a9846fc35f5093499484b6ae74500a
MD5 97b2d67dbb5d9d958e8326efa4f754da
BLAKE2b-256 9d350a0b0da553f43b4734da7c867c698537bc958fc9ea77cc1aa0bc7ee94acb

See more details on using hashes here.

File details

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

File metadata

  • Download URL: townshipcanada-1.0.0-py3-none-any.whl
  • Upload date:
  • Size: 12.1 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.14.3

File hashes

Hashes for townshipcanada-1.0.0-py3-none-any.whl
Algorithm Hash digest
SHA256 2d1db0b9faab5d4d47d00f46efb5a4967898aca38e382f17d4784284f98e3291
MD5 95d4a588652bd61b6aab056970277d35
BLAKE2b-256 eb19886a1c8a67684bd777571a45a68a83a2411cd349e41b1d6a08c23a6c69ce

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