Metadata-Version: 2.4
Name: kogeo
Version: 0.1.0
Summary: Offline, deterministic reverse geocoding for South Korean administrative divisions (WGS84 latitude/longitude to province / city / district / town / village).
Author: kogeo authors
License: MIT
Project-URL: Homepage, https://github.com/WSN1010/KOGEO
Project-URL: Repository, https://github.com/WSN1010/KOGEO
Project-URL: Issues, https://github.com/WSN1010/KOGEO/issues
Keywords: reverse-geocoding,geocoding,korea,south-korea,gis,offline,wgs84,administrative-divisions,shapely
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Scientific/Engineering :: GIS
Requires-Python: >=3.10
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: shapely>=2.0
Provides-Extra: dev
Requires-Dist: pytest>=7.0; extra == "dev"
Dynamic: license-file

<div align="center">

# kogeo

**Offline, deterministic reverse geocoding for South Korean administrative divisions.**

Turn a `(latitude, longitude)` into the exact Korean administrative region that contains it —
province, city/district, town, and village — with **no network, no API key, and no surprises.**

[![PyPI](https://img.shields.io/pypi/v/kogeo.svg)](https://pypi.org/project/kogeo/)
[![Python](https://img.shields.io/pypi/pyversions/kogeo.svg)](https://pypi.org/project/kogeo/)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
[![CI](https://github.com/WSN1010/KOGEO/actions/workflows/ci.yml/badge.svg)](https://github.com/WSN1010/KOGEO/actions/workflows/ci.yml)

**English** · [한국어](README.ko.md) · [日本語](README.ja.md)

</div>

---

## Introduction

Korea has excellent reverse-geocoding services — Kakao, Naver — but they're effectively closed to
developers abroad: getting an API key requires Korean identity verification, which you can't clear
without a Korean phone number. So a developer outside Korea who simply needs to turn a coordinate
into a Korean address often can't.

kogeo exists to close that gap. The administrative boundaries are bundled into the package and every
lookup is pure local geometry — **no API key, no sign-up, no identity check, no network.** Install it
and it works, anywhere, the same way every time:

```python
import kogeo

kogeo.lookup(33.4587, 126.9427)
# → Region(sido='제주특별자치도', sigungu='서귀포시', emd='성산읍', ri='성산리')
```

The coordinate above is **Seongsan Ilchulbong**, a well-known peak on Jeju Island. kogeo resolves it
all the way down to the legal village (`리`), the same hierarchy used in an official Korean address.

> **Scope:** kogeo resolves coordinates **within South Korea only**. Any coordinate outside South Korea returns `None`.

## Install

```bash
pip install kogeo
```

Requires Python 3.10+.

## Quickstart

```python
from kogeo import lookup

# Default: resolve as deep as the data allows.
r = lookup(37.5663, 126.9779)
print(r)               # 서울특별시 중구 태평로1가
print(r.sido)          # 서울특별시
print(r.sigungu)       # 중구
print(r.sigungu_code)  # 11140

# Cap the depth you want.
lookup(37.5663, 126.9779, level="sido")     # → 서울특별시
lookup(37.5663, 126.9779, level="sigungu")  # → 서울특별시 중구

# Outside South Korea → None (kogeo never guesses).
lookup(35.6762, 139.6503)   # Tokyo → None
```

> **Coordinate order is `(latitude, longitude)`** — the order you read them aloud.
> Swapping lat/lon is the single most common geocoding bug, so kogeo fixes the order *and* the names.

## API

### `lookup(lat, lon, level="ri") -> Region | None`

| Param | Type | Description |
|-------|------|-------------|
| `lat` | `float` | WGS84 latitude, `-90 .. 90`. |
| `lon` | `float` | WGS84 longitude, `-180 .. 180`. |
| `level` | `str` | Deepest level to resolve: `"sido"`, `"sigungu"`, `"emd"`, or `"ri"` (default). Parent levels are always included. |

Returns a `Region`, or `None` if the point is in open sea or outside South Korea.
Raises `ValueError` for an out-of-range coordinate or an unknown `level`.

### `Region`

A frozen dataclass carrying the administrative names and their codes:

| Field | Example | | Field | Example |
|-------|---------|---|-------|---------|
| `sido` | `제주특별자치도` | | `sido_code` | `50` |
| `sigungu` | `서귀포시` | | `sigungu_code` | `50130` |
| `emd` | `성산읍` | | `emd_code` | `50130259` |
| `ri` | `성산리` | | `ri_code` | `5013025921` |

A field is `None` when that level does not apply (e.g. `ri` in urban areas) or was capped by `level`.
`str(region)` joins the present names: `"제주특별자치도 서귀포시 성산읍 성산리"`.

## Administrative levels

| Level | Korean | Meaning | Count |
|-------|--------|---------|-------|
| `sido` | 시 / 도 | Province / metropolitan city | 17 |
| `sigungu` | 시 / 군 / 구 | City / county / district | ~250 |
| `emd` | 읍 / 면 / 동 | Town / township / neighborhood | ~3,500 |
| `ri` | 리 | Village (rural areas only) | ~15,000 |

Names are the **legal** administrative names (법정동), the ones used in official addresses.

## How it works

Boundaries are converted to WGS84, simplified, and bundled as compact gzipped WKB. At lookup
time kogeo builds an in-memory [STRtree](https://shapely.readthedocs.io/) and runs a
point-in-polygon test, resolving at the deepest level that contains the point and deriving the
parent regions from the hierarchical code. No projection, no network, no nondeterminism.

## Data & license

- **Code** — MIT. See [LICENSE](LICENSE).
- **Boundary data** — © National Geographic Information Institute (NGII), Ministry of Land,
  Infrastructure and Transport, Republic of Korea: the *Spatial Information Co-use* dataset, via
  [data.go.kr](https://www.data.go.kr/) (open public data, `이용허락범위: 제한 없음`, source
  attribution). Base date **2023-09-15**. Licensed separately from the code.
- Administrative boundaries change over time. The snapshot date is documented, and the
  island/remote fixtures are regression-tested so a data update can't silently break them.

## Contributing

Issues and PRs welcome.
