turkgrid 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,33 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project will be documented in this file.
4
+
5
+ ## [Unreleased]
6
+
7
+ ## [0.1.0] - 2026-11-08
8
+ ### Added
9
+ - **CRS registry** (`crs.py`): 40 EPSG codes for Türkiye - TUREF and ED50 3-degree TM
10
+ (5253-5259, 2319-2325), zone-prefixed 3-degree Gauss-Krüger (5269-5275, 2206-2212),
11
+ WGS 84 and ED50 UTM 35N-38N (32635-32638, 23035-23038), geographic WGS 84 / TUREF /
12
+ ED50 (4326, 5252, 4230) and Web Mercator (3857). `epsg_info`, `supported_epsg_codes`,
13
+ `parse_epsg` (codes, `"EPSG:x"`, OGC URNs/URLs, `CRS84`), `zone_for_lon`,
14
+ `utm_zone_for_lon`, `epsg_for_lon`, `CRSInfo.to_proj4()`.
15
+ - **Transverse Mercator** (`tm.py`): Krüger series to 6th order in n with Newton
16
+ inversion of the conformal latitude (Karney 2011), nanometre-level inside the zones.
17
+ - **Datum shift** (`helmert.py`): EPSG:1784 "ED50 to WGS 84 (30)" (position vector),
18
+ exact inverse for the reverse direction, ED50 <-> TUREF read on GRS80 as PROJ does;
19
+ general `Helmert7` with an exact `apply_inverse` and both rotation conventions.
20
+ - **Transformations** (`transformer.py`): `transform`, `transform_xy`, `to_wgs84`,
21
+ `from_wgs84` and a reusable `Transformer`, for single points, sequences (extra
22
+ ordinates kept) and NumPy arrays (vectorised), with `axes_swapped` /
23
+ `output_swapped` for northing-first Turkish survey data.
24
+ - **CRS detection** (`detect.py`): `detect_crs` scores every CRS reading in both axis
25
+ orders against a coarse outline of Türkiye and the zone widths, with optional
26
+ `near=` location, and returns ranked candidates with reasons, ties and the nearest
27
+ province.
28
+ - **GeoJSON** (`geojson.py`): `reproject_geojson` for collections, features and every
29
+ geometry type, honouring and writing the legacy `crs` member and recomputing `bbox`.
30
+ - **CLI** `turkgrid`: `convert`, `detect` (CSV/TXT with delimiter sniffing, decimal
31
+ commas and header detection), `geojson` and `info`.
32
+ - Test-suite of 198 tests (99 % coverage) including a comparison with PROJ for all
33
+ 1 600 ordered CRS pairs.
@@ -0,0 +1,21 @@
1
+ cff-version: 1.2.0
2
+ message: "If you use this software, please cite it as below."
3
+ authors:
4
+ - family-names: "Eminoğlu"
5
+ given-names: "Yusuf"
6
+ email: "yusufeminoglu@gmail.com"
7
+ orcid: "https://orcid.org/0009-0005-6000-2934"
8
+ title: "turkgrid"
9
+ version: "0.1.0"
10
+ date-released: "2026-11-08"
11
+ license: "MIT"
12
+ repository-code: "https://github.com/YusufEminoglu/turkgrid_sdk"
13
+ url: "https://pypi.org/project/turkgrid/"
14
+ abstract: "Zero-dependency coordinate reference toolkit for Türkiye: TUREF/ED50 3-degree TM and Gauss-Krüger zones, UTM 35N-38N, the EPSG:1784 ED50 to WGS 84 shift, GeoJSON reprojection and heuristic CRS detection, validated against PROJ."
15
+ keywords:
16
+ - geodesy
17
+ - coordinate transformation
18
+ - transverse mercator
19
+ - TUREF
20
+ - ED50
21
+ - Türkiye
turkgrid-0.1.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Yusuf Eminoğlu
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,8 @@
1
+ include LICENSE
2
+ include README.md
3
+ include CHANGELOG.md
4
+ include CITATION.cff
5
+ include pyproject.toml
6
+ include src/turkgrid/py.typed
7
+ recursive-include src/turkgrid *.py
8
+ recursive-include tests *.py
@@ -0,0 +1,293 @@
1
+ Metadata-Version: 2.4
2
+ Name: turkgrid
3
+ Version: 0.1.0
4
+ 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.
5
+ Author-email: Yusuf Eminoğlu <yusufeminoglu@gmail.com>
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://github.com/YusufEminoglu/turkgrid_sdk
8
+ Project-URL: Repository, https://github.com/YusufEminoglu/turkgrid_sdk
9
+ Project-URL: Issues, https://github.com/YusufEminoglu/turkgrid_sdk/issues
10
+ Project-URL: Changelog, https://github.com/YusufEminoglu/turkgrid_sdk/blob/main/CHANGELOG.md
11
+ Keywords: gis,geodesy,crs,coordinate-transformation,reprojection,transverse-mercator,gauss-kruger,utm,turef,ed50,itrf96,epsg,helmert,datum-shift,geojson,turkey,turkiye,netcad,cadastre
12
+ Classifier: Development Status :: 4 - Beta
13
+ Classifier: Programming Language :: Python :: 3
14
+ Classifier: Programming Language :: Python :: 3.9
15
+ Classifier: Programming Language :: Python :: 3.10
16
+ Classifier: Programming Language :: Python :: 3.11
17
+ Classifier: Programming Language :: Python :: 3.12
18
+ Classifier: Programming Language :: Python :: 3.13
19
+ Classifier: Operating System :: OS Independent
20
+ Classifier: Natural Language :: English
21
+ Classifier: Natural Language :: Turkish
22
+ Classifier: Topic :: Scientific/Engineering :: GIS
23
+ Classifier: Topic :: Scientific/Engineering :: Mathematics
24
+ Classifier: Intended Audience :: Science/Research
25
+ Classifier: Intended Audience :: Developers
26
+ Classifier: Typing :: Typed
27
+ Requires-Python: >=3.9
28
+ Description-Content-Type: text/markdown
29
+ License-File: LICENSE
30
+ Provides-Extra: numpy
31
+ Requires-Dist: numpy>=1.20.0; extra == "numpy"
32
+ Provides-Extra: dev
33
+ Requires-Dist: pytest>=7.0.0; extra == "dev"
34
+ Requires-Dist: pytest-cov>=4.0.0; extra == "dev"
35
+ Requires-Dist: ruff>=0.1.0; extra == "dev"
36
+ Requires-Dist: mypy>=1.0.0; extra == "dev"
37
+ Requires-Dist: numpy>=1.20.0; extra == "dev"
38
+ Requires-Dist: pyproj>=3.3.0; extra == "dev"
39
+ Dynamic: license-file
40
+
41
+ <div align="center">
42
+
43
+ # turkgrid
44
+
45
+ [![PyPI version](https://img.shields.io/pypi/v/turkgrid.svg?color=10b981)](https://pypi.org/project/turkgrid/)
46
+ [![Python version support](https://img.shields.io/pypi/pyversions/turkgrid.svg?color=3b82f6)](https://pypi.org/project/turkgrid/)
47
+ [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://github.com/YusufEminoglu/turkgrid_sdk/blob/main/LICENSE)
48
+ [![Code style: Ruff](https://img.shields.io/badge/code%20style-ruff-D7FF64.svg)](https://docs.astral.sh/ruff/)
49
+ [![Test Coverage](https://img.shields.io/badge/coverage-99%25-brightgreen.svg)](#development--geliştirme)
50
+
51
+ **Türkiye için bağımlılıksız koordinat dönüşüm kütüphanesi · Zero-dependency coordinate toolkit for Türkiye**
52
+
53
+ TUREF / ED50 3° TM ve Gauss-Krüger dilimleri · UTM 35N-38N · EPSG:1784 ED50 datum dönüşümü · GeoJSON · CRS tahmini
54
+
55
+ [🇹🇷 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)
56
+
57
+ </div>
58
+
59
+ ---
60
+
61
+ ## 🇹🇷 Türkçe
62
+
63
+ `turkgrid`, Türkiye'de harita, imar planı ve kadastro verilerinde kullanılan koordinat
64
+ sistemleri arasında **saf Python** ile dönüşüm yapar. PROJ, GDAL veya pyproj gerekmez;
65
+ NumPy kuruluysa diziler vektörel olarak işlenir. Sonuçlar PROJ ile karşılaştırılarak
66
+ test edilir (aşağıdaki doğruluk tablosuna bakın).
67
+
68
+ ### Kurulum
69
+
70
+ ```bash
71
+ pip install turkgrid # yalnızca standart kütüphane
72
+ pip install "turkgrid[numpy]" # NumPy dizileriyle vektörel dönüşüm
73
+ pip install "turkgrid[dev]" # testler: pytest, ruff, mypy, numpy, pyproj
74
+ ```
75
+
76
+ Python 3.9+ gerekir; Windows, macOS ve Linux'ta çalışır.
77
+
78
+ ### Hızlı başlangıç
79
+
80
+ ```python
81
+ import turkgrid
82
+
83
+ # TUREF / TM30 (EPSG:5254) -> WGS 84 boylam/enlem
84
+ lon, lat = turkgrid.to_wgs84((412000.0, 4542000.0), 5254)
85
+
86
+ # Eski ED50 / TM30 planını TUREF / TM30'a taşı (EPSG:1784 datum dönüşümü)
87
+ x, y = turkgrid.transform((412000.0, 4542000.0), 2320, 5254)
88
+
89
+ # Dilimden dilime (TM30 -> TM33), yükseklik (z) korunur
90
+ pts = turkgrid.transform([(412000.0, 4542000.0, 35.2)], 5254, 5255)
91
+
92
+ # Türk ölçü pratiği: önce X (yukarı/kuzey), sonra Y (sağa/doğu)
93
+ lon, lat = turkgrid.to_wgs84((4542000.0, 412000.0), 5254, axes_swapped=True)
94
+
95
+ # Boylama göre dilim ve EPSG kodu
96
+ turkgrid.zone_for_lon(29.0) # 30 (merkez meridyen)
97
+ turkgrid.epsg_for_lon(29.0, "TM", "ED50") # 2320
98
+ turkgrid.epsg_info(5254).to_proj4() # '+proj=tmerc +lat_0=0 +lon_0=30 ...'
99
+ ```
100
+
101
+ NumPy dizileri (`(N, 2)` veya `(N, 3)`) ya da ayrı `x`, `y` dizileri tek çağrıda
102
+ dönüştürülür: `turkgrid.transform_xy(x_dizisi, y_dizisi, 2320, 5254)`.
103
+
104
+ ### Koordinat sistemi tahmini
105
+
106
+ Kaynağı belirsiz plan koordinatları için `detect_crs` her olası sistemi dener ve
107
+ gerekçeleriyle sıralı aday listesi döndürür:
108
+
109
+ ```python
110
+ for aday in turkgrid.detect_crs(noktalar, near=(29.06, 40.19)): # near: bilinen yer (Bursa)
111
+ print(aday.epsg, aday.name, aday.score, aday.axes_swapped, aday.place)
112
+ print(" ", "; ".join(aday.reasons))
113
+ ```
114
+
115
+ Koordinatlardan **kesin** anlaşılabilenler: eksen sırası (X/Y ters mi), Gauss-Krüger
116
+ dilim öneki (ör. 10 465 000 → 10. dilim). **Anlaşılamayanlar**: 500 000 m sabitli bir
117
+ TM doğusunun hangi dilime ait olduğu (bütün dilimlere uyar; Türkiye sınırı dışına
118
+ düşen okumalar elenir) ve ED50 ile TUREF farkı (≈ 100-200 m). Bu eşitlikleri
119
+ bozmak için `near=(boylam, enlem)` verin; ED50/TUREF ayrımı için bilinen bir noktayı
120
+ `near_tolerance_m=20` gibi küçük bir toleransla kullanın.
121
+
122
+ ### Komut satırı
123
+
124
+ ```bash
125
+ turkgrid convert --from 5254 --to 4326 412000 4542000
126
+ echo "412000 4542000" | turkgrid convert --from 2320 --to 5254
127
+ turkgrid detect noktalar.csv --near 29.06,40.19 -v # ; ayraç ve ondalık virgül desteklenir
128
+ turkgrid geojson plan.geojson --to 4326 -o plan_wgs84.geojson
129
+ turkgrid info 2320
130
+ ```
131
+
132
+ ### Datum varsayımları
133
+
134
+ * **TUREF = WGS 84.** TUREF (ITRF96, 2005.0) ile WGS 84 Türkiye'de birkaç cm
135
+ içinde örtüşür; EPSG de "TUREF to WGS 84 (1)" (EPSG:5261) dönüşümünü sıfır
136
+ parametreli tanımlar. turkgrid bunu PROJ ile aynı şekilde uygular.
137
+ * **ED50 → WGS 84/TUREF: EPSG:1784 "ED50 to WGS 84 (30)"**, konum vektörü
138
+ (position vector, EPSG yöntem 9606): tx −84.1, ty −101.8, tz −129.7 m, rz +0.468″,
139
+ ölçek +1.05 ppm; HGK'nın yayımladığı EPSG:1783 ile aynı değerler; doğruluk 2 m.
140
+ Ters yön, ileri dönüşümün **tam tersidir** (gidiş-dönüş nanometre düzeyinde kapanır).
141
+ * Yerel dönüşüm parametreleri (ör. belediye nirengi ağı) gerekiyorsa
142
+ `turkgrid.Helmert7` ile kendi 7 parametrenizi uygulayabilirsiniz.
143
+
144
+ ---
145
+
146
+ ## 🇬🇧 English
147
+
148
+ `turkgrid` converts between the coordinate reference systems used for maps, zoning
149
+ plans and cadastre in Türkiye, in **pure Python**: no PROJ, GDAL or pyproj needed, and
150
+ NumPy (optional) vectorises arrays. Every supported pair is tested against PROJ.
151
+
152
+ ### Installation
153
+
154
+ ```bash
155
+ pip install turkgrid # standard library only
156
+ pip install "turkgrid[numpy]" # vectorised NumPy array support
157
+ pip install "turkgrid[dev]" # pytest, ruff, mypy, numpy, pyproj (for the PROJ tests)
158
+ ```
159
+
160
+ ### Quickstart
161
+
162
+ ```python
163
+ import turkgrid
164
+
165
+ lon, lat = turkgrid.to_wgs84((412000.0, 4542000.0), 5254) # TUREF / TM30 -> WGS 84
166
+ x, y = turkgrid.transform((412000.0, 4542000.0), 2320, 5254) # ED50 / TM30 -> TUREF / TM30
167
+ xy = turkgrid.from_wgs84((29.0, 41.0), "EPSG:32635") # WGS 84 -> UTM 35N
168
+ pts = turkgrid.transform([(412000.0, 4542000.0, 35.2)], 5254, 5255) # zone to zone, z kept
169
+
170
+ t = turkgrid.Transformer(2320, 5254) # reusable; t.inverse(), t.description
171
+ gj = turkgrid.reproject_geojson(geojson_dict, 4326) # reads a legacy "crs" member
172
+ best = turkgrid.detect_crs(points)[0] # candidates with score, axes_swapped, reasons, place
173
+ ```
174
+
175
+ * Coordinates are **easting, northing** / **longitude, latitude**. Turkish surveys
176
+ often store northing first; pass `axes_swapped=True` (and `output_swapped=True`).
177
+ * Inputs: one point (tuple, extra ordinates kept), a sequence of points (returns a
178
+ list), or a NumPy array of shape `(2,)` / `(N, 2+)` (returns a new array).
179
+ `transform_xy(x, y, src, dst)` takes floats or arrays.
180
+ * EPSG codes may be `5254`, `"5254"`, `"EPSG:5254"`, an OGC URN/URL or `"CRS84"`.
181
+
182
+ ### CRS detection
183
+
184
+ `detect_crs(points, near=None, near_tolerance_m=25_000)` reads every supported CRS into
185
+ the numbers (in both axis orders), projects a sample to WGS 84 and scores each reading
186
+ by the share of points that fall in Türkiye (coarse outline plus a ~20 km buffer), the
187
+ share inside the CRS's zone, and optional closeness to a known location. Candidates
188
+ come back best-first with human-readable `reasons`, the nearest province centre and
189
+ any ties. Axis order and Gauss-Krüger zone prefixes are unambiguous; the zone of a
190
+ plain TM easting and the ED50/TUREF datum are not, and stay tied unless `near=` breaks
191
+ the tie (use a surveyed point and a ~20 m tolerance to separate the datums).
192
+
193
+ ### Command line
194
+
195
+ ```bash
196
+ turkgrid convert --from 5254 --to 4326 412000 4542000 # or pipe "x y" lines on stdin
197
+ turkgrid detect points.csv [--near lon,lat] [--json] [-v]
198
+ turkgrid geojson in.geojson --to 4326 -o out.geojson # --from defaults to the crs member
199
+ turkgrid info [EPSG]
200
+ ```
201
+
202
+ ### Datum assumptions
203
+
204
+ * **TUREF is taken as WGS 84** (they agree to a few centimetres in Türkiye). EPSG:5261
205
+ "TUREF to WGS 84 (1)" is a zero-parameter transformation, applied as PROJ does.
206
+ * **ED50 → WGS 84 / TUREF uses EPSG:1784 "ED50 to WGS 84 (30)"**, position vector
207
+ (EPSG method 9606): tx −84.1, ty −101.8, tz −129.7 m, rx = ry = 0, rz +0.468″,
208
+ scale +1.05 ppm; same values as EPSG:1783 (published by HGK, the Turkish General
209
+ Directorate of Mapping); stated accuracy 2 m. It is applied on the ellipsoid surface
210
+ (height 0), like PROJ. The reverse direction is the **exact inverse** of the forward
211
+ one (round trips close to nanometres), solved by fixed-point refinement.
212
+ * Plain `pyproj.Transformer.from_crs(2320, 5254)` may choose another operation (e.g.
213
+ via ETRS89 with EPSG:5260, a few cm different, or a generic ED50 set metres away)
214
+ unless an area of interest or the operation is given; turkgrid always uses EPSG:1784.
215
+
216
+ ## Supported EPSG codes · Desteklenen EPSG kodları
217
+
218
+ | Family / Sistem | Central meridian / Merkez meridyen | TUREF (GRS80) | ED50 (Int. 1924) | WGS 84 |
219
+ |---|---|---|---|---|
220
+ | 3° TM, FE 500 000 m, k0 = 1 | 27, 30, 33, 36, 39, 42, 45 | 5253 – 5259 | 2319 – 2325 | – |
221
+ | 3° Gauss-Krüger, FE = zone·10⁶ + 500 000 m (zones 9 – 15) | 27 … 45 | 5269 – 5275 | 2206 – 2212 | – |
222
+ | 6° UTM 35N – 38N, k0 = 0.9996 | 27, 33, 39, 45 | – | 23035 – 23038 | 32635 – 32638 |
223
+ | Geographic / Coğrafi | – | 5252 | 4230 | 4326 |
224
+ | Web Mercator | – | – | – | 3857 |
225
+
226
+ All 40 definitions (names, central meridians, false eastings, ellipsoids) are checked
227
+ against PROJ's EPSG database (v12.029) in the test-suite.
228
+
229
+ ## Accuracy vs PROJ · PROJ ile karşılaştırma
230
+
231
+ Measured with pyproj 3.8 / PROJ 9.8.1 over **all 1 600 ordered pairs** of the 40 codes,
232
+ 200 random points per source CRS spread over Türkiye (maximum horizontal difference):
233
+
234
+ | Pair type / Dönüşüm türü | Max difference vs PROJ |
235
+ |---|---|
236
+ | Same datum: TM ↔ GK ↔ UTM ↔ geographic ↔ Web Mercator (incl. TUREF ↔ WGS 84) | < 0.00001 mm (≈ 6 nm) |
237
+ | ED50 → TUREF / WGS 84 (EPSG:1784 forward) | < 0.00001 mm (≈ 8 nm) |
238
+ | TUREF / WGS 84 → ED50 (EPSG:1784 reverse) | ≤ 1.13 mm ¹ |
239
+ | Krüger series (6th order) inside / far outside a zone | nanometres (Karney 2011) |
240
+
241
+ ¹ Not an error in turkgrid: PROJ's reverse operation evaluates the WGS 84 point at
242
+ height 0 and is therefore not an exact inverse of its forward operation — a PROJ
243
+ ED50 → WGS 84 → ED50 round trip fails to close by up to 1.08 mm. turkgrid's round trip
244
+ closes to < 0.00001 mm. The 2 m accuracy of EPSG:1784 itself dominates either way.
245
+
246
+ Speed (one core): pure Python ~100 000 points/s for TM ↔ WGS 84 and ~50 000 points/s
247
+ with the ED50 shift; with NumPy arrays ~800 000 points/s including the ED50 shift.
248
+
249
+ ## Migration from cad2geo / NCZ2Geo · Geçiş notu
250
+
251
+ turkgrid unifies the projection code of the author's
252
+ [cad2geo](https://github.com/YusufEminoglu/cad2geo_sdk) (`cad2geo.reproject`,
253
+ `cad2geo.crs`) and [NCZ2Geo](https://github.com/YusufEminoglu/NCZ2Geo_sdk)
254
+ (`ncz2geo._projection`, `ncz2geo.transformer`). Neither package is changed by this
255
+ release; they can later add `turkgrid>=0.1` as a dependency and keep their public
256
+ functions as thin wrappers:
257
+
258
+ | Existing function | turkgrid equivalent |
259
+ |---|---|
260
+ | `cad2geo.to_wgs84(x, y, epsg, axes_swapped)` | `turkgrid.to_wgs84((x, y), epsg, axes_swapped=...)` |
261
+ | `cad2geo.reproject_geojson(gj, epsg, axes_swapped, ndigits)` | `turkgrid.reproject_geojson(gj, 4326, epsg, axes_swapped=..., precision=ndigits)` |
262
+ | `cad2geo.supported_epsg_codes()` / `cad2geo.crs` tables | `turkgrid.supported_epsg_codes()`, `turkgrid.epsg_info()`, `turkgrid.epsg_for_lon()` |
263
+ | `cad2geo.detect_crs(text, coords)` (coordinate part) | `turkgrid.detect_crs(coords)` (keep the metadata-text parsing in cad2geo) |
264
+ | `ncz2geo._projection.to_wgs84 / from_wgs84` | `turkgrid.transform_xy(x, y, srid, 4326)` and back |
265
+ | `ncz2geo.transformer.reproject_coordinate` | `turkgrid.transform_xy(x, y, src, dst)` |
266
+ | `ncz2geo.transformer.Helmert7Params`, `helmert_7parameter_transform` | `turkgrid.Helmert7(...).apply(x, y, z)` |
267
+
268
+ Behavioural differences to note when switching: the TM series is 6th order instead of
269
+ 4th (sub-nanometre instead of sub-millimetre), ED50 inversion is exact, and TUREF
270
+ ↔ WGS 84 follows PROJ's EPSG:5261 handling. turkgrid covers every code of both
271
+ packages (a superset: NCZ2Geo bundles 26 of these 40 codes, cad2geo 37 without Web
272
+ Mercator); NCZ2Geo keeps its WKT/PROJJSON definitions for its file writers.
273
+
274
+ ## Development · Geliştirme
275
+
276
+ ```bash
277
+ git clone https://github.com/YusufEminoglu/turkgrid_sdk.git
278
+ cd turkgrid_sdk
279
+ pip install -e ".[dev]"
280
+ python -m pytest # 198 tests incl. doctests; PROJ tests skip without pyproj
281
+ python -m ruff check . && python -m ruff format --check src tests && python -m mypy src
282
+ ```
283
+
284
+ ## License · Lisans
285
+
286
+ MIT © 2026 Yusuf Eminoğlu. Parts of the code originate from the author's own cad2geo
287
+ and NCZ2Geo packages, which are published under GPL-2.0-or-later; as the sole author
288
+ and copyright holder of that code, the author releases it here under the MIT license.
289
+ The MIT license is GPL-compatible, so both packages can depend on turkgrid.
290
+
291
+ **References:** C. F. F. Karney (2011), *Transverse Mercator with an accuracy of a few
292
+ nanometers*, J. Geodesy 85:475-485 · IOGP EPSG Geodetic Parameter Dataset, operations
293
+ 1783, 1784, 5260, 5261 · IOGP Guidance Note 7-2 (method 9606, position vector).
@@ -0,0 +1,253 @@
1
+ <div align="center">
2
+
3
+ # turkgrid
4
+
5
+ [![PyPI version](https://img.shields.io/pypi/v/turkgrid.svg?color=10b981)](https://pypi.org/project/turkgrid/)
6
+ [![Python version support](https://img.shields.io/pypi/pyversions/turkgrid.svg?color=3b82f6)](https://pypi.org/project/turkgrid/)
7
+ [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://github.com/YusufEminoglu/turkgrid_sdk/blob/main/LICENSE)
8
+ [![Code style: Ruff](https://img.shields.io/badge/code%20style-ruff-D7FF64.svg)](https://docs.astral.sh/ruff/)
9
+ [![Test Coverage](https://img.shields.io/badge/coverage-99%25-brightgreen.svg)](#development--geliştirme)
10
+
11
+ **Türkiye için bağımlılıksız koordinat dönüşüm kütüphanesi · Zero-dependency coordinate toolkit for Türkiye**
12
+
13
+ TUREF / ED50 3° TM ve Gauss-Krüger dilimleri · UTM 35N-38N · EPSG:1784 ED50 datum dönüşümü · GeoJSON · CRS tahmini
14
+
15
+ [🇹🇷 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)
16
+
17
+ </div>
18
+
19
+ ---
20
+
21
+ ## 🇹🇷 Türkçe
22
+
23
+ `turkgrid`, Türkiye'de harita, imar planı ve kadastro verilerinde kullanılan koordinat
24
+ sistemleri arasında **saf Python** ile dönüşüm yapar. PROJ, GDAL veya pyproj gerekmez;
25
+ NumPy kuruluysa diziler vektörel olarak işlenir. Sonuçlar PROJ ile karşılaştırılarak
26
+ test edilir (aşağıdaki doğruluk tablosuna bakın).
27
+
28
+ ### Kurulum
29
+
30
+ ```bash
31
+ pip install turkgrid # yalnızca standart kütüphane
32
+ pip install "turkgrid[numpy]" # NumPy dizileriyle vektörel dönüşüm
33
+ pip install "turkgrid[dev]" # testler: pytest, ruff, mypy, numpy, pyproj
34
+ ```
35
+
36
+ Python 3.9+ gerekir; Windows, macOS ve Linux'ta çalışır.
37
+
38
+ ### Hızlı başlangıç
39
+
40
+ ```python
41
+ import turkgrid
42
+
43
+ # TUREF / TM30 (EPSG:5254) -> WGS 84 boylam/enlem
44
+ lon, lat = turkgrid.to_wgs84((412000.0, 4542000.0), 5254)
45
+
46
+ # Eski ED50 / TM30 planını TUREF / TM30'a taşı (EPSG:1784 datum dönüşümü)
47
+ x, y = turkgrid.transform((412000.0, 4542000.0), 2320, 5254)
48
+
49
+ # Dilimden dilime (TM30 -> TM33), yükseklik (z) korunur
50
+ pts = turkgrid.transform([(412000.0, 4542000.0, 35.2)], 5254, 5255)
51
+
52
+ # Türk ölçü pratiği: önce X (yukarı/kuzey), sonra Y (sağa/doğu)
53
+ lon, lat = turkgrid.to_wgs84((4542000.0, 412000.0), 5254, axes_swapped=True)
54
+
55
+ # Boylama göre dilim ve EPSG kodu
56
+ turkgrid.zone_for_lon(29.0) # 30 (merkez meridyen)
57
+ turkgrid.epsg_for_lon(29.0, "TM", "ED50") # 2320
58
+ turkgrid.epsg_info(5254).to_proj4() # '+proj=tmerc +lat_0=0 +lon_0=30 ...'
59
+ ```
60
+
61
+ NumPy dizileri (`(N, 2)` veya `(N, 3)`) ya da ayrı `x`, `y` dizileri tek çağrıda
62
+ dönüştürülür: `turkgrid.transform_xy(x_dizisi, y_dizisi, 2320, 5254)`.
63
+
64
+ ### Koordinat sistemi tahmini
65
+
66
+ Kaynağı belirsiz plan koordinatları için `detect_crs` her olası sistemi dener ve
67
+ gerekçeleriyle sıralı aday listesi döndürür:
68
+
69
+ ```python
70
+ for aday in turkgrid.detect_crs(noktalar, near=(29.06, 40.19)): # near: bilinen yer (Bursa)
71
+ print(aday.epsg, aday.name, aday.score, aday.axes_swapped, aday.place)
72
+ print(" ", "; ".join(aday.reasons))
73
+ ```
74
+
75
+ Koordinatlardan **kesin** anlaşılabilenler: eksen sırası (X/Y ters mi), Gauss-Krüger
76
+ dilim öneki (ör. 10 465 000 → 10. dilim). **Anlaşılamayanlar**: 500 000 m sabitli bir
77
+ TM doğusunun hangi dilime ait olduğu (bütün dilimlere uyar; Türkiye sınırı dışına
78
+ düşen okumalar elenir) ve ED50 ile TUREF farkı (≈ 100-200 m). Bu eşitlikleri
79
+ bozmak için `near=(boylam, enlem)` verin; ED50/TUREF ayrımı için bilinen bir noktayı
80
+ `near_tolerance_m=20` gibi küçük bir toleransla kullanın.
81
+
82
+ ### Komut satırı
83
+
84
+ ```bash
85
+ turkgrid convert --from 5254 --to 4326 412000 4542000
86
+ echo "412000 4542000" | turkgrid convert --from 2320 --to 5254
87
+ turkgrid detect noktalar.csv --near 29.06,40.19 -v # ; ayraç ve ondalık virgül desteklenir
88
+ turkgrid geojson plan.geojson --to 4326 -o plan_wgs84.geojson
89
+ turkgrid info 2320
90
+ ```
91
+
92
+ ### Datum varsayımları
93
+
94
+ * **TUREF = WGS 84.** TUREF (ITRF96, 2005.0) ile WGS 84 Türkiye'de birkaç cm
95
+ içinde örtüşür; EPSG de "TUREF to WGS 84 (1)" (EPSG:5261) dönüşümünü sıfır
96
+ parametreli tanımlar. turkgrid bunu PROJ ile aynı şekilde uygular.
97
+ * **ED50 → WGS 84/TUREF: EPSG:1784 "ED50 to WGS 84 (30)"**, konum vektörü
98
+ (position vector, EPSG yöntem 9606): tx −84.1, ty −101.8, tz −129.7 m, rz +0.468″,
99
+ ölçek +1.05 ppm; HGK'nın yayımladığı EPSG:1783 ile aynı değerler; doğruluk 2 m.
100
+ Ters yön, ileri dönüşümün **tam tersidir** (gidiş-dönüş nanometre düzeyinde kapanır).
101
+ * Yerel dönüşüm parametreleri (ör. belediye nirengi ağı) gerekiyorsa
102
+ `turkgrid.Helmert7` ile kendi 7 parametrenizi uygulayabilirsiniz.
103
+
104
+ ---
105
+
106
+ ## 🇬🇧 English
107
+
108
+ `turkgrid` converts between the coordinate reference systems used for maps, zoning
109
+ plans and cadastre in Türkiye, in **pure Python**: no PROJ, GDAL or pyproj needed, and
110
+ NumPy (optional) vectorises arrays. Every supported pair is tested against PROJ.
111
+
112
+ ### Installation
113
+
114
+ ```bash
115
+ pip install turkgrid # standard library only
116
+ pip install "turkgrid[numpy]" # vectorised NumPy array support
117
+ pip install "turkgrid[dev]" # pytest, ruff, mypy, numpy, pyproj (for the PROJ tests)
118
+ ```
119
+
120
+ ### Quickstart
121
+
122
+ ```python
123
+ import turkgrid
124
+
125
+ lon, lat = turkgrid.to_wgs84((412000.0, 4542000.0), 5254) # TUREF / TM30 -> WGS 84
126
+ x, y = turkgrid.transform((412000.0, 4542000.0), 2320, 5254) # ED50 / TM30 -> TUREF / TM30
127
+ xy = turkgrid.from_wgs84((29.0, 41.0), "EPSG:32635") # WGS 84 -> UTM 35N
128
+ pts = turkgrid.transform([(412000.0, 4542000.0, 35.2)], 5254, 5255) # zone to zone, z kept
129
+
130
+ t = turkgrid.Transformer(2320, 5254) # reusable; t.inverse(), t.description
131
+ gj = turkgrid.reproject_geojson(geojson_dict, 4326) # reads a legacy "crs" member
132
+ best = turkgrid.detect_crs(points)[0] # candidates with score, axes_swapped, reasons, place
133
+ ```
134
+
135
+ * Coordinates are **easting, northing** / **longitude, latitude**. Turkish surveys
136
+ often store northing first; pass `axes_swapped=True` (and `output_swapped=True`).
137
+ * Inputs: one point (tuple, extra ordinates kept), a sequence of points (returns a
138
+ list), or a NumPy array of shape `(2,)` / `(N, 2+)` (returns a new array).
139
+ `transform_xy(x, y, src, dst)` takes floats or arrays.
140
+ * EPSG codes may be `5254`, `"5254"`, `"EPSG:5254"`, an OGC URN/URL or `"CRS84"`.
141
+
142
+ ### CRS detection
143
+
144
+ `detect_crs(points, near=None, near_tolerance_m=25_000)` reads every supported CRS into
145
+ the numbers (in both axis orders), projects a sample to WGS 84 and scores each reading
146
+ by the share of points that fall in Türkiye (coarse outline plus a ~20 km buffer), the
147
+ share inside the CRS's zone, and optional closeness to a known location. Candidates
148
+ come back best-first with human-readable `reasons`, the nearest province centre and
149
+ any ties. Axis order and Gauss-Krüger zone prefixes are unambiguous; the zone of a
150
+ plain TM easting and the ED50/TUREF datum are not, and stay tied unless `near=` breaks
151
+ the tie (use a surveyed point and a ~20 m tolerance to separate the datums).
152
+
153
+ ### Command line
154
+
155
+ ```bash
156
+ turkgrid convert --from 5254 --to 4326 412000 4542000 # or pipe "x y" lines on stdin
157
+ turkgrid detect points.csv [--near lon,lat] [--json] [-v]
158
+ turkgrid geojson in.geojson --to 4326 -o out.geojson # --from defaults to the crs member
159
+ turkgrid info [EPSG]
160
+ ```
161
+
162
+ ### Datum assumptions
163
+
164
+ * **TUREF is taken as WGS 84** (they agree to a few centimetres in Türkiye). EPSG:5261
165
+ "TUREF to WGS 84 (1)" is a zero-parameter transformation, applied as PROJ does.
166
+ * **ED50 → WGS 84 / TUREF uses EPSG:1784 "ED50 to WGS 84 (30)"**, position vector
167
+ (EPSG method 9606): tx −84.1, ty −101.8, tz −129.7 m, rx = ry = 0, rz +0.468″,
168
+ scale +1.05 ppm; same values as EPSG:1783 (published by HGK, the Turkish General
169
+ Directorate of Mapping); stated accuracy 2 m. It is applied on the ellipsoid surface
170
+ (height 0), like PROJ. The reverse direction is the **exact inverse** of the forward
171
+ one (round trips close to nanometres), solved by fixed-point refinement.
172
+ * Plain `pyproj.Transformer.from_crs(2320, 5254)` may choose another operation (e.g.
173
+ via ETRS89 with EPSG:5260, a few cm different, or a generic ED50 set metres away)
174
+ unless an area of interest or the operation is given; turkgrid always uses EPSG:1784.
175
+
176
+ ## Supported EPSG codes · Desteklenen EPSG kodları
177
+
178
+ | Family / Sistem | Central meridian / Merkez meridyen | TUREF (GRS80) | ED50 (Int. 1924) | WGS 84 |
179
+ |---|---|---|---|---|
180
+ | 3° TM, FE 500 000 m, k0 = 1 | 27, 30, 33, 36, 39, 42, 45 | 5253 – 5259 | 2319 – 2325 | – |
181
+ | 3° Gauss-Krüger, FE = zone·10⁶ + 500 000 m (zones 9 – 15) | 27 … 45 | 5269 – 5275 | 2206 – 2212 | – |
182
+ | 6° UTM 35N – 38N, k0 = 0.9996 | 27, 33, 39, 45 | – | 23035 – 23038 | 32635 – 32638 |
183
+ | Geographic / Coğrafi | – | 5252 | 4230 | 4326 |
184
+ | Web Mercator | – | – | – | 3857 |
185
+
186
+ All 40 definitions (names, central meridians, false eastings, ellipsoids) are checked
187
+ against PROJ's EPSG database (v12.029) in the test-suite.
188
+
189
+ ## Accuracy vs PROJ · PROJ ile karşılaştırma
190
+
191
+ Measured with pyproj 3.8 / PROJ 9.8.1 over **all 1 600 ordered pairs** of the 40 codes,
192
+ 200 random points per source CRS spread over Türkiye (maximum horizontal difference):
193
+
194
+ | Pair type / Dönüşüm türü | Max difference vs PROJ |
195
+ |---|---|
196
+ | Same datum: TM ↔ GK ↔ UTM ↔ geographic ↔ Web Mercator (incl. TUREF ↔ WGS 84) | < 0.00001 mm (≈ 6 nm) |
197
+ | ED50 → TUREF / WGS 84 (EPSG:1784 forward) | < 0.00001 mm (≈ 8 nm) |
198
+ | TUREF / WGS 84 → ED50 (EPSG:1784 reverse) | ≤ 1.13 mm ¹ |
199
+ | Krüger series (6th order) inside / far outside a zone | nanometres (Karney 2011) |
200
+
201
+ ¹ Not an error in turkgrid: PROJ's reverse operation evaluates the WGS 84 point at
202
+ height 0 and is therefore not an exact inverse of its forward operation — a PROJ
203
+ ED50 → WGS 84 → ED50 round trip fails to close by up to 1.08 mm. turkgrid's round trip
204
+ closes to < 0.00001 mm. The 2 m accuracy of EPSG:1784 itself dominates either way.
205
+
206
+ Speed (one core): pure Python ~100 000 points/s for TM ↔ WGS 84 and ~50 000 points/s
207
+ with the ED50 shift; with NumPy arrays ~800 000 points/s including the ED50 shift.
208
+
209
+ ## Migration from cad2geo / NCZ2Geo · Geçiş notu
210
+
211
+ turkgrid unifies the projection code of the author's
212
+ [cad2geo](https://github.com/YusufEminoglu/cad2geo_sdk) (`cad2geo.reproject`,
213
+ `cad2geo.crs`) and [NCZ2Geo](https://github.com/YusufEminoglu/NCZ2Geo_sdk)
214
+ (`ncz2geo._projection`, `ncz2geo.transformer`). Neither package is changed by this
215
+ release; they can later add `turkgrid>=0.1` as a dependency and keep their public
216
+ functions as thin wrappers:
217
+
218
+ | Existing function | turkgrid equivalent |
219
+ |---|---|
220
+ | `cad2geo.to_wgs84(x, y, epsg, axes_swapped)` | `turkgrid.to_wgs84((x, y), epsg, axes_swapped=...)` |
221
+ | `cad2geo.reproject_geojson(gj, epsg, axes_swapped, ndigits)` | `turkgrid.reproject_geojson(gj, 4326, epsg, axes_swapped=..., precision=ndigits)` |
222
+ | `cad2geo.supported_epsg_codes()` / `cad2geo.crs` tables | `turkgrid.supported_epsg_codes()`, `turkgrid.epsg_info()`, `turkgrid.epsg_for_lon()` |
223
+ | `cad2geo.detect_crs(text, coords)` (coordinate part) | `turkgrid.detect_crs(coords)` (keep the metadata-text parsing in cad2geo) |
224
+ | `ncz2geo._projection.to_wgs84 / from_wgs84` | `turkgrid.transform_xy(x, y, srid, 4326)` and back |
225
+ | `ncz2geo.transformer.reproject_coordinate` | `turkgrid.transform_xy(x, y, src, dst)` |
226
+ | `ncz2geo.transformer.Helmert7Params`, `helmert_7parameter_transform` | `turkgrid.Helmert7(...).apply(x, y, z)` |
227
+
228
+ Behavioural differences to note when switching: the TM series is 6th order instead of
229
+ 4th (sub-nanometre instead of sub-millimetre), ED50 inversion is exact, and TUREF
230
+ ↔ WGS 84 follows PROJ's EPSG:5261 handling. turkgrid covers every code of both
231
+ packages (a superset: NCZ2Geo bundles 26 of these 40 codes, cad2geo 37 without Web
232
+ Mercator); NCZ2Geo keeps its WKT/PROJJSON definitions for its file writers.
233
+
234
+ ## Development · Geliştirme
235
+
236
+ ```bash
237
+ git clone https://github.com/YusufEminoglu/turkgrid_sdk.git
238
+ cd turkgrid_sdk
239
+ pip install -e ".[dev]"
240
+ python -m pytest # 198 tests incl. doctests; PROJ tests skip without pyproj
241
+ python -m ruff check . && python -m ruff format --check src tests && python -m mypy src
242
+ ```
243
+
244
+ ## License · Lisans
245
+
246
+ MIT © 2026 Yusuf Eminoğlu. Parts of the code originate from the author's own cad2geo
247
+ and NCZ2Geo packages, which are published under GPL-2.0-or-later; as the sole author
248
+ and copyright holder of that code, the author releases it here under the MIT license.
249
+ The MIT license is GPL-compatible, so both packages can depend on turkgrid.
250
+
251
+ **References:** C. F. F. Karney (2011), *Transverse Mercator with an accuracy of a few
252
+ nanometers*, J. Geodesy 85:475-485 · IOGP EPSG Geodetic Parameter Dataset, operations
253
+ 1783, 1784, 5260, 5261 · IOGP Guidance Note 7-2 (method 9606, position vector).