taigeotrans 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,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Chien-Hsun Chang & TaiGeotrans Contributors
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,181 @@
1
+ Metadata-Version: 2.4
2
+ Name: taigeotrans
3
+ Version: 0.1.0
4
+ Summary: Taiwan Geospatial Transformer - 台灣地址與座標轉換工具
5
+ Author: TaiGeotrans Contributors
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://github.com/KageRyo/TaiGeotrans
8
+ Project-URL: Repository, https://github.com/KageRyo/TaiGeotrans
9
+ Project-URL: Issues, https://github.com/KageRyo/TaiGeotrans/issues
10
+ Keywords: taiwan,geospatial,coordinate,transform,TWD97,WGS84,geocoding
11
+ Classifier: Development Status :: 4 - Beta
12
+ Classifier: Intended Audience :: Developers
13
+ Classifier: Programming Language :: Python :: 3
14
+ Classifier: Programming Language :: Python :: 3 :: Only
15
+ Classifier: Programming Language :: Python :: 3.11
16
+ Classifier: Programming Language :: Python :: 3.12
17
+ Classifier: Programming Language :: Python :: 3.13
18
+ Classifier: Topic :: Scientific/Engineering :: GIS
19
+ Classifier: Typing :: Typed
20
+ Requires-Python: >=3.11
21
+ Description-Content-Type: text/markdown
22
+ License-File: LICENSE
23
+ Requires-Dist: pyproj>=3.6.0
24
+ Requires-Dist: pydantic>=2.0.0
25
+ Provides-Extra: geocoding
26
+ Requires-Dist: httpx>=0.27.0; extra == "geocoding"
27
+ Provides-Extra: dataframe
28
+ Requires-Dist: pandas>=2.0.0; extra == "dataframe"
29
+ Provides-Extra: progress
30
+ Requires-Dist: tqdm>=4.66.0; extra == "progress"
31
+ Provides-Extra: cli
32
+ Requires-Dist: httpx>=0.27.0; extra == "cli"
33
+ Requires-Dist: pandas>=2.0.0; extra == "cli"
34
+ Requires-Dist: python-dotenv>=1.0.0; extra == "cli"
35
+ Requires-Dist: rich>=13.0.0; extra == "cli"
36
+ Requires-Dist: tqdm>=4.66.0; extra == "cli"
37
+ Requires-Dist: typer>=0.12.0; extra == "cli"
38
+ Provides-Extra: all
39
+ Requires-Dist: httpx>=0.27.0; extra == "all"
40
+ Requires-Dist: pandas>=2.0.0; extra == "all"
41
+ Requires-Dist: python-dotenv>=1.0.0; extra == "all"
42
+ Requires-Dist: rich>=13.0.0; extra == "all"
43
+ Requires-Dist: tqdm>=4.66.0; extra == "all"
44
+ Requires-Dist: typer>=0.12.0; extra == "all"
45
+ Provides-Extra: dev
46
+ Requires-Dist: pytest>=8.0.0; extra == "dev"
47
+ Requires-Dist: pytest-cov>=4.1.0; extra == "dev"
48
+ Requires-Dist: build>=1.2.0; extra == "dev"
49
+ Requires-Dist: twine>=5.0.0; extra == "dev"
50
+ Requires-Dist: ruff>=0.3.0; extra == "dev"
51
+ Requires-Dist: mypy>=1.8.0; extra == "dev"
52
+ Dynamic: license-file
53
+
54
+ # TaiGeotrans
55
+
56
+ [![CI](https://github.com/KageRyo/TaiGeotrans/actions/workflows/ci.yml/badge.svg)](https://github.com/KageRyo/TaiGeotrans/actions/workflows/ci.yml)
57
+ [![PyPI version](https://img.shields.io/pypi/v/taigeotrans?logo=pypi&logoColor=white)](https://pypi.org/project/taigeotrans/)
58
+ [![Python 3.11+](https://img.shields.io/badge/python-3.11%2B-3776AB?logo=python&logoColor=white)](https://www.python.org/)
59
+ [![License: MIT](https://img.shields.io/badge/license-MIT-green.svg)](LICENSE)
60
+
61
+ TaiGeotrans 是一個台灣地址與座標轉換工具,支援:
62
+
63
+ - WGS84(EPSG:4326)與 TWD97(EPSG:3826)雙向轉換
64
+ - 透過 TGOS 將台灣地址轉成座標
65
+ - 批量處理、pandas DataFrame 與 GeoJSON
66
+ - 命令列操作
67
+
68
+ ## 安裝
69
+
70
+ 只使用座標轉換時:
71
+
72
+ ```bash
73
+ python -m pip install taigeotrans
74
+ ```
75
+
76
+ 需要地址定位、DataFrame 或 CLI 時,再安裝對應功能:
77
+
78
+ ```bash
79
+ python -m pip install "taigeotrans[geocoding]"
80
+ python -m pip install "taigeotrans[dataframe]"
81
+ python -m pip install "taigeotrans[cli]"
82
+ ```
83
+
84
+ 也可以一次安裝全部功能:
85
+
86
+ ```bash
87
+ python -m pip install "taigeotrans[all]"
88
+ ```
89
+
90
+ ## Python API
91
+
92
+ ```python
93
+ from taigeotrans import TaiGeotrans
94
+
95
+ tg = TaiGeotrans()
96
+
97
+ # WGS84 -> TWD97
98
+ result = tg.transform_lonlat(121.5654, 25.0330)
99
+ print(result.twd97_x, result.twd97_y)
100
+
101
+ # TWD97 -> WGS84
102
+ result = tg.transform_twd97(303891.54, 2773226.67)
103
+ print(result.wgs84_lon, result.wgs84_lat)
104
+ ```
105
+
106
+ ### 地址定位
107
+
108
+ 地址定位使用 TGOS,需要 AppID 與 APIKey。請先在 TGOS 申請這兩項資料,
109
+ 再透過環境變數提供:
110
+
111
+ ```bash
112
+ export TGOS_APP_ID="your-app-id"
113
+ export TGOS_API_KEY="your-api-key"
114
+ ```
115
+
116
+ 或直接傳給 `TaiGeotrans`:
117
+
118
+ ```python
119
+ tg = TaiGeotrans(
120
+ tgos_app_id="your-app-id",
121
+ tgos_api_key="your-api-key",
122
+ )
123
+ result = tg.geocode("臺北市中山區松江路469巷4號")
124
+ print(result.matched_address, result.twd97_x, result.twd97_y)
125
+ ```
126
+
127
+ Python API 不會自動讀取 `.env`。CLI 會讀取目前工作目錄的 `.env`,可以複製
128
+ `.env.example` 作為範本;實際 key 不要提交到 Git。
129
+
130
+ ### 批量處理
131
+
132
+ ```python
133
+ addresses = ["嘉義縣民雄鄉中樂路55號", "臺北市信義區市府路1號"]
134
+ coords = [(120.4278, 23.5521), (121.5654, 25.0330)]
135
+
136
+ address_results = tg.batch_geocode(addresses, show_progress=False)
137
+ coordinate_results = tg.batch_transform_lonlat(coords, show_progress=False)
138
+ df = tg.batch_transform_lonlat_to_dataframe(coords, show_progress=False)
139
+ ```
140
+
141
+ DataFrame 方法需要 `taigeotrans[dataframe]`。進度列使用 `tqdm`;沒有安裝時,
142
+ 批量方法仍可執行,只是不顯示進度列。
143
+
144
+ ## CLI
145
+
146
+ ```bash
147
+ python -m pip install "taigeotrans[cli]"
148
+
149
+ taigeotrans transform 121.5654 25.0330
150
+ taigeotrans transform 121.5654 25.0330 --format json
151
+ taigeotrans geocode "臺北市信義區市府路1號"
152
+ taigeotrans batch-geocode addresses.txt -o results.csv
153
+ taigeotrans batch-transform coords.csv -o results.csv
154
+ ```
155
+
156
+ `addresses.txt` 每行一個地址;`coords.csv` 需要包含 `lon,lat` 欄位。執行
157
+ `taigeotrans --help` 查看完整指令。
158
+
159
+ ## 結果
160
+
161
+ 每個操作都回傳 `GeocodeResult`,包含輸入值、WGS84/TWD97 座標、來源與狀態。
162
+ 狀態可能是 `success`、`out_of_bounds`、`invalid` 或 `failed`。
163
+
164
+ ## 開發
165
+
166
+ ```bash
167
+ python -m pip install -e ".[dev,all]"
168
+ python -m pytest
169
+ python -m ruff check src tests
170
+ python -m ruff format --check src tests
171
+ python -m mypy src
172
+ python -m build
173
+ python -m twine check dist/*
174
+ ```
175
+
176
+ CI 會測試 Python 3.11–3.13。推送 `vMAJOR.MINOR.PATCH` tag 後,release workflow
177
+ 會建置套件、發佈到 PyPI,並建立 GitHub Release。
178
+
179
+ ## 授權
180
+
181
+ MIT License,請參考 [LICENSE](LICENSE)。
@@ -0,0 +1,128 @@
1
+ # TaiGeotrans
2
+
3
+ [![CI](https://github.com/KageRyo/TaiGeotrans/actions/workflows/ci.yml/badge.svg)](https://github.com/KageRyo/TaiGeotrans/actions/workflows/ci.yml)
4
+ [![PyPI version](https://img.shields.io/pypi/v/taigeotrans?logo=pypi&logoColor=white)](https://pypi.org/project/taigeotrans/)
5
+ [![Python 3.11+](https://img.shields.io/badge/python-3.11%2B-3776AB?logo=python&logoColor=white)](https://www.python.org/)
6
+ [![License: MIT](https://img.shields.io/badge/license-MIT-green.svg)](LICENSE)
7
+
8
+ TaiGeotrans 是一個台灣地址與座標轉換工具,支援:
9
+
10
+ - WGS84(EPSG:4326)與 TWD97(EPSG:3826)雙向轉換
11
+ - 透過 TGOS 將台灣地址轉成座標
12
+ - 批量處理、pandas DataFrame 與 GeoJSON
13
+ - 命令列操作
14
+
15
+ ## 安裝
16
+
17
+ 只使用座標轉換時:
18
+
19
+ ```bash
20
+ python -m pip install taigeotrans
21
+ ```
22
+
23
+ 需要地址定位、DataFrame 或 CLI 時,再安裝對應功能:
24
+
25
+ ```bash
26
+ python -m pip install "taigeotrans[geocoding]"
27
+ python -m pip install "taigeotrans[dataframe]"
28
+ python -m pip install "taigeotrans[cli]"
29
+ ```
30
+
31
+ 也可以一次安裝全部功能:
32
+
33
+ ```bash
34
+ python -m pip install "taigeotrans[all]"
35
+ ```
36
+
37
+ ## Python API
38
+
39
+ ```python
40
+ from taigeotrans import TaiGeotrans
41
+
42
+ tg = TaiGeotrans()
43
+
44
+ # WGS84 -> TWD97
45
+ result = tg.transform_lonlat(121.5654, 25.0330)
46
+ print(result.twd97_x, result.twd97_y)
47
+
48
+ # TWD97 -> WGS84
49
+ result = tg.transform_twd97(303891.54, 2773226.67)
50
+ print(result.wgs84_lon, result.wgs84_lat)
51
+ ```
52
+
53
+ ### 地址定位
54
+
55
+ 地址定位使用 TGOS,需要 AppID 與 APIKey。請先在 TGOS 申請這兩項資料,
56
+ 再透過環境變數提供:
57
+
58
+ ```bash
59
+ export TGOS_APP_ID="your-app-id"
60
+ export TGOS_API_KEY="your-api-key"
61
+ ```
62
+
63
+ 或直接傳給 `TaiGeotrans`:
64
+
65
+ ```python
66
+ tg = TaiGeotrans(
67
+ tgos_app_id="your-app-id",
68
+ tgos_api_key="your-api-key",
69
+ )
70
+ result = tg.geocode("臺北市中山區松江路469巷4號")
71
+ print(result.matched_address, result.twd97_x, result.twd97_y)
72
+ ```
73
+
74
+ Python API 不會自動讀取 `.env`。CLI 會讀取目前工作目錄的 `.env`,可以複製
75
+ `.env.example` 作為範本;實際 key 不要提交到 Git。
76
+
77
+ ### 批量處理
78
+
79
+ ```python
80
+ addresses = ["嘉義縣民雄鄉中樂路55號", "臺北市信義區市府路1號"]
81
+ coords = [(120.4278, 23.5521), (121.5654, 25.0330)]
82
+
83
+ address_results = tg.batch_geocode(addresses, show_progress=False)
84
+ coordinate_results = tg.batch_transform_lonlat(coords, show_progress=False)
85
+ df = tg.batch_transform_lonlat_to_dataframe(coords, show_progress=False)
86
+ ```
87
+
88
+ DataFrame 方法需要 `taigeotrans[dataframe]`。進度列使用 `tqdm`;沒有安裝時,
89
+ 批量方法仍可執行,只是不顯示進度列。
90
+
91
+ ## CLI
92
+
93
+ ```bash
94
+ python -m pip install "taigeotrans[cli]"
95
+
96
+ taigeotrans transform 121.5654 25.0330
97
+ taigeotrans transform 121.5654 25.0330 --format json
98
+ taigeotrans geocode "臺北市信義區市府路1號"
99
+ taigeotrans batch-geocode addresses.txt -o results.csv
100
+ taigeotrans batch-transform coords.csv -o results.csv
101
+ ```
102
+
103
+ `addresses.txt` 每行一個地址;`coords.csv` 需要包含 `lon,lat` 欄位。執行
104
+ `taigeotrans --help` 查看完整指令。
105
+
106
+ ## 結果
107
+
108
+ 每個操作都回傳 `GeocodeResult`,包含輸入值、WGS84/TWD97 座標、來源與狀態。
109
+ 狀態可能是 `success`、`out_of_bounds`、`invalid` 或 `failed`。
110
+
111
+ ## 開發
112
+
113
+ ```bash
114
+ python -m pip install -e ".[dev,all]"
115
+ python -m pytest
116
+ python -m ruff check src tests
117
+ python -m ruff format --check src tests
118
+ python -m mypy src
119
+ python -m build
120
+ python -m twine check dist/*
121
+ ```
122
+
123
+ CI 會測試 Python 3.11–3.13。推送 `vMAJOR.MINOR.PATCH` tag 後,release workflow
124
+ 會建置套件、發佈到 PyPI,並建立 GitHub Release。
125
+
126
+ ## 授權
127
+
128
+ MIT License,請參考 [LICENSE](LICENSE)。
@@ -0,0 +1,102 @@
1
+ [build-system]
2
+ requires = ["setuptools>=77.0.3"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "taigeotrans"
7
+ version = "0.1.0"
8
+ description = "Taiwan Geospatial Transformer - 台灣地址與座標轉換工具"
9
+ readme = "README.md"
10
+ requires-python = ">=3.11"
11
+ license = "MIT"
12
+ license-files = ["LICENSE"]
13
+ authors = [
14
+ {name = "TaiGeotrans Contributors"}
15
+ ]
16
+ keywords = ["taiwan", "geospatial", "coordinate", "transform", "TWD97", "WGS84", "geocoding"]
17
+ classifiers = [
18
+ "Development Status :: 4 - Beta",
19
+ "Intended Audience :: Developers",
20
+ "Programming Language :: Python :: 3",
21
+ "Programming Language :: Python :: 3 :: Only",
22
+ "Programming Language :: Python :: 3.11",
23
+ "Programming Language :: Python :: 3.12",
24
+ "Programming Language :: Python :: 3.13",
25
+ "Topic :: Scientific/Engineering :: GIS",
26
+ "Typing :: Typed",
27
+ ]
28
+ dependencies = [
29
+ "pyproj>=3.6.0",
30
+ "pydantic>=2.0.0",
31
+ ]
32
+
33
+ [project.optional-dependencies]
34
+ geocoding = [
35
+ "httpx>=0.27.0",
36
+ ]
37
+ dataframe = [
38
+ "pandas>=2.0.0",
39
+ ]
40
+ progress = [
41
+ "tqdm>=4.66.0",
42
+ ]
43
+ cli = [
44
+ "httpx>=0.27.0",
45
+ "pandas>=2.0.0",
46
+ "python-dotenv>=1.0.0",
47
+ "rich>=13.0.0",
48
+ "tqdm>=4.66.0",
49
+ "typer>=0.12.0",
50
+ ]
51
+ all = [
52
+ "httpx>=0.27.0",
53
+ "pandas>=2.0.0",
54
+ "python-dotenv>=1.0.0",
55
+ "rich>=13.0.0",
56
+ "tqdm>=4.66.0",
57
+ "typer>=0.12.0",
58
+ ]
59
+ dev = [
60
+ "pytest>=8.0.0",
61
+ "pytest-cov>=4.1.0",
62
+ "build>=1.2.0",
63
+ "twine>=5.0.0",
64
+ "ruff>=0.3.0",
65
+ "mypy>=1.8.0",
66
+ ]
67
+
68
+ [project.scripts]
69
+ taigeotrans = "taigeotrans.cli:main"
70
+
71
+ [project.urls]
72
+ Homepage = "https://github.com/KageRyo/TaiGeotrans"
73
+ Repository = "https://github.com/KageRyo/TaiGeotrans"
74
+ Issues = "https://github.com/KageRyo/TaiGeotrans/issues"
75
+
76
+ [tool.setuptools.packages.find]
77
+ where = ["src"]
78
+
79
+ [tool.setuptools.package-data]
80
+ taigeotrans = ["py.typed"]
81
+
82
+ [tool.ruff]
83
+ line-length = 100
84
+ target-version = "py311"
85
+
86
+ [tool.ruff.lint]
87
+ select = ["E", "F", "I", "N", "W", "UP"]
88
+ ignore = []
89
+
90
+ [tool.mypy]
91
+ python_version = "3.11"
92
+ plugins = ["pydantic.mypy"]
93
+ warn_return_any = true
94
+ warn_unused_configs = true
95
+ ignore_missing_imports = true
96
+
97
+ [tool.pytest.ini_options]
98
+ testpaths = ["tests"]
99
+ python_files = ["test_*.py"]
100
+ python_classes = ["Test*"]
101
+ python_functions = ["test_*"]
102
+ addopts = "-v --cov=taigeotrans --cov-report=term-missing"
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,13 @@
1
+ """
2
+ TaiGeotrans - Taiwan Geospatial Transformer
3
+ 台灣地址與座標轉換工具
4
+
5
+ 基於內政地理資訊圖資雲整合服務平台 (TGOS) 實現的輕量級地址、
6
+ 經緯度 (WGS84:EPSG:4326) 及 TWD97 (EPSG:3826) 轉換器。
7
+ """
8
+
9
+ from taigeotrans.core import TaiGeotrans
10
+ from taigeotrans.models import GeocodeResult, TransformStatus
11
+
12
+ __version__ = "0.1.0"
13
+ __all__ = ["TaiGeotrans", "GeocodeResult", "TransformStatus"]
@@ -0,0 +1,182 @@
1
+ """Typer implementation for the optional TaiGeotrans CLI extra."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import json
6
+ from pathlib import Path
7
+
8
+ import pandas as pd
9
+ import typer
10
+ from rich.console import Console
11
+ from rich.table import Table
12
+
13
+ from taigeotrans.core import TaiGeotrans
14
+ from taigeotrans.models import GeocodeResult, TransformStatus
15
+
16
+ app = typer.Typer(
17
+ name="taigeotrans",
18
+ help="TaiGeotrans - Taiwan address and coordinate transformation tool",
19
+ add_completion=False,
20
+ )
21
+ console = Console()
22
+
23
+
24
+ @app.command()
25
+ def geocode(
26
+ address: str = typer.Argument(..., help="Taiwan address to geocode"),
27
+ output_format: str = typer.Option(
28
+ "table",
29
+ "--format",
30
+ "-f",
31
+ help="Output format: table, json, csv",
32
+ ),
33
+ ) -> None:
34
+ """Geocode a Taiwan address to TWD97 coordinates."""
35
+ result = TaiGeotrans().geocode(address)
36
+ _print_single_result(result, output_format)
37
+
38
+
39
+ @app.command()
40
+ def batch_geocode(
41
+ input_file: Path = typer.Argument(..., help="Input file with one address per line"),
42
+ output_file: Path | None = typer.Option(
43
+ None,
44
+ "--output",
45
+ "-o",
46
+ help="Output file path",
47
+ ),
48
+ output_format: str = typer.Option(
49
+ "csv",
50
+ "--format",
51
+ "-f",
52
+ help="Output format: csv, json, geojson",
53
+ ),
54
+ ) -> None:
55
+ """Geocode multiple Taiwan addresses."""
56
+ if not input_file.exists():
57
+ raise typer.BadParameter(f"File not found: {input_file}")
58
+
59
+ addresses = [
60
+ line.strip() for line in input_file.read_text(encoding="utf-8").splitlines() if line.strip()
61
+ ]
62
+ if not addresses:
63
+ console.print("[yellow]No valid addresses in file[/yellow]")
64
+ return
65
+
66
+ converter = TaiGeotrans()
67
+ results = converter.batch_geocode(addresses, show_progress=True)
68
+ _save_batch_results(results, output_file, output_format, converter)
69
+
70
+
71
+ @app.command()
72
+ def transform(
73
+ lon: float = typer.Argument(..., help="WGS84 longitude"),
74
+ lat: float = typer.Argument(..., help="WGS84 latitude"),
75
+ output_format: str = typer.Option(
76
+ "table",
77
+ "--format",
78
+ "-f",
79
+ help="Output format: table, json, csv",
80
+ ),
81
+ ) -> None:
82
+ """Transform WGS84 coordinates to TWD97."""
83
+ result = TaiGeotrans().transform_lonlat(lon, lat)
84
+ _print_single_result(result, output_format)
85
+
86
+
87
+ @app.command()
88
+ def batch_transform(
89
+ input_file: Path = typer.Argument(..., help="CSV file with lon and lat columns"),
90
+ output_file: Path | None = typer.Option(
91
+ None,
92
+ "--output",
93
+ "-o",
94
+ help="Output file path",
95
+ ),
96
+ output_format: str = typer.Option(
97
+ "csv",
98
+ "--format",
99
+ "-f",
100
+ help="Output format: csv, json, geojson",
101
+ ),
102
+ ) -> None:
103
+ """Transform multiple WGS84 coordinate pairs."""
104
+ if not input_file.exists():
105
+ raise typer.BadParameter(f"File not found: {input_file}")
106
+
107
+ df = pd.read_csv(input_file)
108
+ if "lon" not in df.columns or "lat" not in df.columns:
109
+ raise typer.BadParameter("CSV file must contain 'lon' and 'lat' columns")
110
+
111
+ coords = [(row.lon, row.lat) for row in df.itertuples(index=False)]
112
+ converter = TaiGeotrans()
113
+ results = converter.batch_transform_lonlat(coords, show_progress=True)
114
+ _save_batch_results(results, output_file, output_format, converter)
115
+
116
+
117
+ @app.command()
118
+ def version() -> None:
119
+ """Display version information."""
120
+ from taigeotrans import __version__
121
+
122
+ console.print(f"TaiGeotrans version {__version__}")
123
+
124
+
125
+ def _print_single_result(result: GeocodeResult, output_format: str) -> None:
126
+ if output_format == "json":
127
+ console.print_json(json.dumps(result.to_dict(), ensure_ascii=False, indent=2))
128
+ elif output_format == "csv":
129
+ frame = pd.DataFrame([result.to_dict()])
130
+ console.print(frame.to_csv(index=False))
131
+ elif output_format == "table":
132
+ table = Table(title="Transformation Result")
133
+ for key, value in result.to_dict().items():
134
+ table.add_row(key, str(value))
135
+ console.print(table)
136
+ _print_status(result)
137
+ else:
138
+ raise typer.BadParameter(f"Unsupported output format: {output_format}")
139
+
140
+
141
+ def _print_status(result: GeocodeResult) -> None:
142
+ if result.status == TransformStatus.SUCCESS:
143
+ console.print("[green]Success[/green]")
144
+ elif result.status == TransformStatus.OUT_OF_BOUNDS:
145
+ console.print("[yellow]Warning: coordinates are outside Taiwan mainland bounds[/yellow]")
146
+ else:
147
+ console.print(f"[red]Failed: {result.error_message}[/red]")
148
+
149
+
150
+ def _save_batch_results(
151
+ results: list[GeocodeResult],
152
+ output_file: Path | None,
153
+ output_format: str,
154
+ converter: TaiGeotrans,
155
+ ) -> None:
156
+ if output_file is None:
157
+ for result in results[:10]:
158
+ console.print_json(json.dumps(result.to_dict(), ensure_ascii=False))
159
+ return
160
+
161
+ if output_format == "json":
162
+ output_file.write_text(
163
+ json.dumps([result.to_dict() for result in results], ensure_ascii=False, indent=2),
164
+ encoding="utf-8",
165
+ )
166
+ elif output_format == "geojson":
167
+ output_file.write_text(
168
+ json.dumps(
169
+ converter.to_geojson(results, filter_failed=False), ensure_ascii=False, indent=2
170
+ ),
171
+ encoding="utf-8",
172
+ )
173
+ elif output_format == "csv":
174
+ pd.DataFrame([result.to_dict() for result in results]).to_csv(
175
+ output_file,
176
+ index=False,
177
+ encoding="utf-8-sig",
178
+ )
179
+ else:
180
+ raise typer.BadParameter(f"Unsupported output format: {output_format}")
181
+
182
+ console.print(f"[green]Saved to {output_file} ({output_format})[/green]")
@@ -0,0 +1,36 @@
1
+ """Command-line entry point with an actionable optional-dependency error."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import sys
6
+
7
+
8
+ def main() -> int:
9
+ """Run the Typer CLI and return a process exit code."""
10
+ try:
11
+ from dotenv import load_dotenv
12
+
13
+ load_dotenv()
14
+ from taigeotrans._cli_app import app
15
+ except ImportError as exc:
16
+ print(
17
+ "The CLI requires optional dependencies. Install them with "
18
+ "`python -m pip install 'taigeotrans[cli]'`.",
19
+ file=sys.stderr,
20
+ )
21
+ print(f"Missing dependency: {exc}", file=sys.stderr)
22
+ return 1
23
+
24
+ try:
25
+ app()
26
+ except KeyboardInterrupt:
27
+ print("\nInterrupted", file=sys.stderr)
28
+ return 130
29
+ except Exception as exc:
30
+ print(f"Error: {exc}", file=sys.stderr)
31
+ return 1
32
+ return 0
33
+
34
+
35
+ if __name__ == "__main__":
36
+ raise SystemExit(main())