Metadata-Version: 2.4
Name: turkgrid
Version: 0.1.0
Summary: Zero-dependency coordinate toolkit for Türkiye: TUREF/ED50 TM and Gauss-Krüger zones, UTM 35N-38N, the EPSG:1784 ED50 shift and CRS detection.
Author-email: Yusuf Eminoğlu <yusufeminoglu@gmail.com>
License-Expression: MIT
Project-URL: Homepage, https://github.com/YusufEminoglu/turkgrid_sdk
Project-URL: Repository, https://github.com/YusufEminoglu/turkgrid_sdk
Project-URL: Issues, https://github.com/YusufEminoglu/turkgrid_sdk/issues
Project-URL: Changelog, https://github.com/YusufEminoglu/turkgrid_sdk/blob/main/CHANGELOG.md
Keywords: gis,geodesy,crs,coordinate-transformation,reprojection,transverse-mercator,gauss-kruger,utm,turef,ed50,itrf96,epsg,helmert,datum-shift,geojson,turkey,turkiye,netcad,cadastre
Classifier: Development Status :: 4 - Beta
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.9
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: Operating System :: OS Independent
Classifier: Natural Language :: English
Classifier: Natural Language :: Turkish
Classifier: Topic :: Scientific/Engineering :: GIS
Classifier: Topic :: Scientific/Engineering :: Mathematics
Classifier: Intended Audience :: Science/Research
Classifier: Intended Audience :: Developers
Classifier: Typing :: Typed
Requires-Python: >=3.9
Description-Content-Type: text/markdown
License-File: LICENSE
Provides-Extra: numpy
Requires-Dist: numpy>=1.20.0; extra == "numpy"
Provides-Extra: dev
Requires-Dist: pytest>=7.0.0; extra == "dev"
Requires-Dist: pytest-cov>=4.0.0; extra == "dev"
Requires-Dist: ruff>=0.1.0; extra == "dev"
Requires-Dist: mypy>=1.0.0; extra == "dev"
Requires-Dist: numpy>=1.20.0; extra == "dev"
Requires-Dist: pyproj>=3.3.0; extra == "dev"
Dynamic: license-file

<div align="center">

# turkgrid

[![PyPI version](https://img.shields.io/pypi/v/turkgrid.svg?color=10b981)](https://pypi.org/project/turkgrid/)
[![Python version support](https://img.shields.io/pypi/pyversions/turkgrid.svg?color=3b82f6)](https://pypi.org/project/turkgrid/)
[![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://github.com/YusufEminoglu/turkgrid_sdk/blob/main/LICENSE)
[![Code style: Ruff](https://img.shields.io/badge/code%20style-ruff-D7FF64.svg)](https://docs.astral.sh/ruff/)
[![Test Coverage](https://img.shields.io/badge/coverage-99%25-brightgreen.svg)](#development--geliştirme)

**Türkiye için bağımlılıksız koordinat dönüşüm kütüphanesi · Zero-dependency coordinate toolkit for Türkiye**

TUREF / ED50 3° TM ve Gauss-Krüger dilimleri · UTM 35N-38N · EPSG:1784 ED50 datum dönüşümü · GeoJSON · CRS tahmini

[🇹🇷 Türkçe](#-türkçe) • [🇬🇧 English](#-english) • [📦 PyPI](https://pypi.org/project/turkgrid/) • [💻 GitHub](https://github.com/YusufEminoglu/turkgrid_sdk) • [🐛 Issues](https://github.com/YusufEminoglu/turkgrid_sdk/issues)

</div>

---

## 🇹🇷 Türkçe

`turkgrid`, Türkiye'de harita, imar planı ve kadastro verilerinde kullanılan koordinat
sistemleri arasında **saf Python** ile dönüşüm yapar. PROJ, GDAL veya pyproj gerekmez;
NumPy kuruluysa diziler vektörel olarak işlenir. Sonuçlar PROJ ile karşılaştırılarak
test edilir (aşağıdaki doğruluk tablosuna bakın).

### Kurulum

```bash
pip install turkgrid              # yalnızca standart kütüphane
pip install "turkgrid[numpy]"     # NumPy dizileriyle vektörel dönüşüm
pip install "turkgrid[dev]"       # testler: pytest, ruff, mypy, numpy, pyproj
```

Python 3.9+ gerekir; Windows, macOS ve Linux'ta çalışır.

### Hızlı başlangıç

```python
import turkgrid

# TUREF / TM30 (EPSG:5254) -> WGS 84 boylam/enlem
lon, lat = turkgrid.to_wgs84((412000.0, 4542000.0), 5254)

# Eski ED50 / TM30 planını TUREF / TM30'a taşı (EPSG:1784 datum dönüşümü)
x, y = turkgrid.transform((412000.0, 4542000.0), 2320, 5254)

# Dilimden dilime (TM30 -> TM33), yükseklik (z) korunur
pts = turkgrid.transform([(412000.0, 4542000.0, 35.2)], 5254, 5255)

# Türk ölçü pratiği: önce X (yukarı/kuzey), sonra Y (sağa/doğu)
lon, lat = turkgrid.to_wgs84((4542000.0, 412000.0), 5254, axes_swapped=True)

# Boylama göre dilim ve EPSG kodu
turkgrid.zone_for_lon(29.0)             # 30 (merkez meridyen)
turkgrid.epsg_for_lon(29.0, "TM", "ED50")  # 2320
turkgrid.epsg_info(5254).to_proj4()     # '+proj=tmerc +lat_0=0 +lon_0=30 ...'
```

NumPy dizileri (`(N, 2)` veya `(N, 3)`) ya da ayrı `x`, `y` dizileri tek çağrıda
dönüştürülür: `turkgrid.transform_xy(x_dizisi, y_dizisi, 2320, 5254)`.

### Koordinat sistemi tahmini

Kaynağı belirsiz plan koordinatları için `detect_crs` her olası sistemi dener ve
gerekçeleriyle sıralı aday listesi döndürür:

```python
for aday in turkgrid.detect_crs(noktalar, near=(29.06, 40.19)):  # near: bilinen yer (Bursa)
    print(aday.epsg, aday.name, aday.score, aday.axes_swapped, aday.place)
    print("   ", "; ".join(aday.reasons))
```

Koordinatlardan **kesin** anlaşılabilenler: eksen sırası (X/Y ters mi), Gauss-Krüger
dilim öneki (ör. 10 465 000 → 10. dilim). **Anlaşılamayanlar**: 500 000 m sabitli bir
TM doğusunun hangi dilime ait olduğu (bütün dilimlere uyar; Türkiye sınırı dışına
düşen okumalar elenir) ve ED50 ile TUREF farkı (≈ 100-200 m). Bu eşitlikleri
bozmak için `near=(boylam, enlem)` verin; ED50/TUREF ayrımı için bilinen bir noktayı
`near_tolerance_m=20` gibi küçük bir toleransla kullanın.

### Komut satırı

```bash
turkgrid convert --from 5254 --to 4326 412000 4542000
echo "412000 4542000" | turkgrid convert --from 2320 --to 5254
turkgrid detect noktalar.csv --near 29.06,40.19 -v   # ; ayraç ve ondalık virgül desteklenir
turkgrid geojson plan.geojson --to 4326 -o plan_wgs84.geojson
turkgrid info 2320
```

### Datum varsayımları

* **TUREF = WGS 84.** TUREF (ITRF96, 2005.0) ile WGS 84 Türkiye'de birkaç cm
  içinde örtüşür; EPSG de "TUREF to WGS 84 (1)" (EPSG:5261) dönüşümünü sıfır
  parametreli tanımlar. turkgrid bunu PROJ ile aynı şekilde uygular.
* **ED50 → WGS 84/TUREF: EPSG:1784 "ED50 to WGS 84 (30)"**, konum vektörü
  (position vector, EPSG yöntem 9606): tx −84.1, ty −101.8, tz −129.7 m, rz +0.468″,
  ölçek +1.05 ppm; HGK'nın yayımladığı EPSG:1783 ile aynı değerler; doğruluk 2 m.
  Ters yön, ileri dönüşümün **tam tersidir** (gidiş-dönüş nanometre düzeyinde kapanır).
* Yerel dönüşüm parametreleri (ör. belediye nirengi ağı) gerekiyorsa
  `turkgrid.Helmert7` ile kendi 7 parametrenizi uygulayabilirsiniz.

---

## 🇬🇧 English

`turkgrid` converts between the coordinate reference systems used for maps, zoning
plans and cadastre in Türkiye, in **pure Python**: no PROJ, GDAL or pyproj needed, and
NumPy (optional) vectorises arrays. Every supported pair is tested against PROJ.

### Installation

```bash
pip install turkgrid              # standard library only
pip install "turkgrid[numpy]"     # vectorised NumPy array support
pip install "turkgrid[dev]"       # pytest, ruff, mypy, numpy, pyproj (for the PROJ tests)
```

### Quickstart

```python
import turkgrid

lon, lat = turkgrid.to_wgs84((412000.0, 4542000.0), 5254)        # TUREF / TM30 -> WGS 84
x, y = turkgrid.transform((412000.0, 4542000.0), 2320, 5254)     # ED50 / TM30 -> TUREF / TM30
xy = turkgrid.from_wgs84((29.0, 41.0), "EPSG:32635")             # WGS 84 -> UTM 35N
pts = turkgrid.transform([(412000.0, 4542000.0, 35.2)], 5254, 5255)  # zone to zone, z kept

t = turkgrid.Transformer(2320, 5254)   # reusable; t.inverse(), t.description
gj = turkgrid.reproject_geojson(geojson_dict, 4326)   # reads a legacy "crs" member
best = turkgrid.detect_crs(points)[0]  # candidates with score, axes_swapped, reasons, place
```

* Coordinates are **easting, northing** / **longitude, latitude**. Turkish surveys
  often store northing first; pass `axes_swapped=True` (and `output_swapped=True`).
* Inputs: one point (tuple, extra ordinates kept), a sequence of points (returns a
  list), or a NumPy array of shape `(2,)` / `(N, 2+)` (returns a new array).
  `transform_xy(x, y, src, dst)` takes floats or arrays.
* EPSG codes may be `5254`, `"5254"`, `"EPSG:5254"`, an OGC URN/URL or `"CRS84"`.

### CRS detection

`detect_crs(points, near=None, near_tolerance_m=25_000)` reads every supported CRS into
the numbers (in both axis orders), projects a sample to WGS 84 and scores each reading
by the share of points that fall in Türkiye (coarse outline plus a ~20 km buffer), the
share inside the CRS's zone, and optional closeness to a known location. Candidates
come back best-first with human-readable `reasons`, the nearest province centre and
any ties. Axis order and Gauss-Krüger zone prefixes are unambiguous; the zone of a
plain TM easting and the ED50/TUREF datum are not, and stay tied unless `near=` breaks
the tie (use a surveyed point and a ~20 m tolerance to separate the datums).

### Command line

```bash
turkgrid convert --from 5254 --to 4326 412000 4542000   # or pipe "x y" lines on stdin
turkgrid detect points.csv [--near lon,lat] [--json] [-v]
turkgrid geojson in.geojson --to 4326 -o out.geojson     # --from defaults to the crs member
turkgrid info [EPSG]
```

### Datum assumptions

* **TUREF is taken as WGS 84** (they agree to a few centimetres in Türkiye). EPSG:5261
  "TUREF to WGS 84 (1)" is a zero-parameter transformation, applied as PROJ does.
* **ED50 → WGS 84 / TUREF uses EPSG:1784 "ED50 to WGS 84 (30)"**, position vector
  (EPSG method 9606): tx −84.1, ty −101.8, tz −129.7 m, rx = ry = 0, rz +0.468″,
  scale +1.05 ppm; same values as EPSG:1783 (published by HGK, the Turkish General
  Directorate of Mapping); stated accuracy 2 m. It is applied on the ellipsoid surface
  (height 0), like PROJ. The reverse direction is the **exact inverse** of the forward
  one (round trips close to nanometres), solved by fixed-point refinement.
* Plain `pyproj.Transformer.from_crs(2320, 5254)` may choose another operation (e.g.
  via ETRS89 with EPSG:5260, a few cm different, or a generic ED50 set metres away)
  unless an area of interest or the operation is given; turkgrid always uses EPSG:1784.

## Supported EPSG codes · Desteklenen EPSG kodları

| Family / Sistem | Central meridian / Merkez meridyen | TUREF (GRS80) | ED50 (Int. 1924) | WGS 84 |
|---|---|---|---|---|
| 3° TM, FE 500 000 m, k0 = 1 | 27, 30, 33, 36, 39, 42, 45 | 5253 – 5259 | 2319 – 2325 | – |
| 3° Gauss-Krüger, FE = zone·10⁶ + 500 000 m (zones 9 – 15) | 27 … 45 | 5269 – 5275 | 2206 – 2212 | – |
| 6° UTM 35N – 38N, k0 = 0.9996 | 27, 33, 39, 45 | – | 23035 – 23038 | 32635 – 32638 |
| Geographic / Coğrafi | – | 5252 | 4230 | 4326 |
| Web Mercator | – | – | – | 3857 |

All 40 definitions (names, central meridians, false eastings, ellipsoids) are checked
against PROJ's EPSG database (v12.029) in the test-suite.

## Accuracy vs PROJ · PROJ ile karşılaştırma

Measured with pyproj 3.8 / PROJ 9.8.1 over **all 1 600 ordered pairs** of the 40 codes,
200 random points per source CRS spread over Türkiye (maximum horizontal difference):

| Pair type / Dönüşüm türü | Max difference vs PROJ |
|---|---|
| Same datum: TM ↔ GK ↔ UTM ↔ geographic ↔ Web Mercator (incl. TUREF ↔ WGS 84) | < 0.00001 mm (≈ 6 nm) |
| ED50 → TUREF / WGS 84 (EPSG:1784 forward) | < 0.00001 mm (≈ 8 nm) |
| TUREF / WGS 84 → ED50 (EPSG:1784 reverse) | ≤ 1.13 mm ¹ |
| Krüger series (6th order) inside / far outside a zone | nanometres (Karney 2011) |

¹ Not an error in turkgrid: PROJ's reverse operation evaluates the WGS 84 point at
height 0 and is therefore not an exact inverse of its forward operation — a PROJ
ED50 → WGS 84 → ED50 round trip fails to close by up to 1.08 mm. turkgrid's round trip
closes to < 0.00001 mm. The 2 m accuracy of EPSG:1784 itself dominates either way.

Speed (one core): pure Python ~100 000 points/s for TM ↔ WGS 84 and ~50 000 points/s
with the ED50 shift; with NumPy arrays ~800 000 points/s including the ED50 shift.

## Migration from cad2geo / NCZ2Geo · Geçiş notu

turkgrid unifies the projection code of the author's
[cad2geo](https://github.com/YusufEminoglu/cad2geo_sdk) (`cad2geo.reproject`,
`cad2geo.crs`) and [NCZ2Geo](https://github.com/YusufEminoglu/NCZ2Geo_sdk)
(`ncz2geo._projection`, `ncz2geo.transformer`). Neither package is changed by this
release; they can later add `turkgrid>=0.1` as a dependency and keep their public
functions as thin wrappers:

| Existing function | turkgrid equivalent |
|---|---|
| `cad2geo.to_wgs84(x, y, epsg, axes_swapped)` | `turkgrid.to_wgs84((x, y), epsg, axes_swapped=...)` |
| `cad2geo.reproject_geojson(gj, epsg, axes_swapped, ndigits)` | `turkgrid.reproject_geojson(gj, 4326, epsg, axes_swapped=..., precision=ndigits)` |
| `cad2geo.supported_epsg_codes()` / `cad2geo.crs` tables | `turkgrid.supported_epsg_codes()`, `turkgrid.epsg_info()`, `turkgrid.epsg_for_lon()` |
| `cad2geo.detect_crs(text, coords)` (coordinate part) | `turkgrid.detect_crs(coords)` (keep the metadata-text parsing in cad2geo) |
| `ncz2geo._projection.to_wgs84 / from_wgs84` | `turkgrid.transform_xy(x, y, srid, 4326)` and back |
| `ncz2geo.transformer.reproject_coordinate` | `turkgrid.transform_xy(x, y, src, dst)` |
| `ncz2geo.transformer.Helmert7Params`, `helmert_7parameter_transform` | `turkgrid.Helmert7(...).apply(x, y, z)` |

Behavioural differences to note when switching: the TM series is 6th order instead of
4th (sub-nanometre instead of sub-millimetre), ED50 inversion is exact, and TUREF
↔ WGS 84 follows PROJ's EPSG:5261 handling. turkgrid covers every code of both
packages (a superset: NCZ2Geo bundles 26 of these 40 codes, cad2geo 37 without Web
Mercator); NCZ2Geo keeps its WKT/PROJJSON definitions for its file writers.

## Development · Geliştirme

```bash
git clone https://github.com/YusufEminoglu/turkgrid_sdk.git
cd turkgrid_sdk
pip install -e ".[dev]"
python -m pytest            # 198 tests incl. doctests; PROJ tests skip without pyproj
python -m ruff check . && python -m ruff format --check src tests && python -m mypy src
```

## License · Lisans

MIT © 2026 Yusuf Eminoğlu. Parts of the code originate from the author's own cad2geo
and NCZ2Geo packages, which are published under GPL-2.0-or-later; as the sole author
and copyright holder of that code, the author releases it here under the MIT license.
The MIT license is GPL-compatible, so both packages can depend on turkgrid.

**References:** C. F. F. Karney (2011), *Transverse Mercator with an accuracy of a few
nanometers*, J. Geodesy 85:475-485 · IOGP EPSG Geodetic Parameter Dataset, operations
1783, 1784, 5260, 5261 · IOGP Guidance Note 7-2 (method 9606, position vector).
