shw-geocoder 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 zx
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,4 @@
1
+ include README.md
2
+ include LICENSE
3
+ include pyproject.toml
4
+ recursive-include shw_geocoder *.py
@@ -0,0 +1,96 @@
1
+ Metadata-Version: 2.4
2
+ Name: shw-geocoder
3
+ Version: 0.1.0
4
+ Summary: Geocoder client for SHSMI address_search API
5
+ Author: zx
6
+ License: MIT
7
+ Keywords: geocoder,geocoding,address,shsmi,csv
8
+ Classifier: Development Status :: 3 - Alpha
9
+ Classifier: Intended Audience :: Developers
10
+ Classifier: License :: OSI Approved :: MIT License
11
+ Classifier: Operating System :: OS Independent
12
+ Classifier: Programming Language :: Python :: 3
13
+ Classifier: Programming Language :: Python :: 3 :: Only
14
+ Classifier: Programming Language :: Python :: 3.8
15
+ Classifier: Programming Language :: Python :: 3.9
16
+ Classifier: Programming Language :: Python :: 3.10
17
+ Classifier: Programming Language :: Python :: 3.11
18
+ Classifier: Programming Language :: Python :: 3.12
19
+ Classifier: Topic :: Scientific/Engineering :: GIS
20
+ Classifier: Topic :: Utilities
21
+ Requires-Python: >=3.8
22
+ Description-Content-Type: text/markdown
23
+ License-File: LICENSE
24
+ Requires-Dist: requests
25
+ Dynamic: license-file
26
+
27
+ # shw-geocoder
28
+
29
+ A minimal PyPI package that wraps the SHSMI `address_search` geocoding API. **THIS PACKAGE IS SPECIFIC TO CERTAIN GEOCODING APIS AND IS NOT UNIVERSAL.**
30
+
31
+ ## Install
32
+
33
+ ```bash
34
+ pip install shw-geocoder
35
+ ```
36
+
37
+ For local development:
38
+
39
+ ```bash
40
+ pip install -e .
41
+ ```
42
+
43
+ ## Python API
44
+
45
+ ```python
46
+ from shw_geocoder import geocode
47
+
48
+ result = geocode("your-address", "geocoder-url")
49
+ print(result["data"][0]["pointX"], result["data"][0]["pointY"])
50
+ ```
51
+
52
+ `geocode(address, base, result_count=3)` returns the raw JSON returned by the
53
+ service. When the address cannot be resolved, the payload's `count` field is `0`
54
+ and `data` is empty.
55
+
56
+ ## CLI
57
+
58
+ The package exposes a `shw-geocoder` command.
59
+
60
+ ### Single address
61
+
62
+ ```bash
63
+ shw-geocoder --base "geocoder-url" \
64
+ --address "your-address"
65
+ ```
66
+
67
+ Prints `x, y, accuracy, poi, room_count` to stdout.
68
+
69
+ ### Batch (CSV in -> CSV out + log)
70
+
71
+ ```bash
72
+ shw-geocoder --base "geocoder-url" \
73
+ --input addrs.csv \
74
+ --address-column address \
75
+ --output out.csv \
76
+ --log run.log
77
+ ```
78
+
79
+ - `out.csv` keeps every original column from `addrs.csv` and appends five new
80
+ columns: `x, y, accuracy, poi, room_count`. Failed rows leave these blank.
81
+ - `run.log` is a plain-text summary containing start/end time, elapsed seconds,
82
+ total / success / failed counts, and a breakdown by `accuracy` value.
83
+
84
+ ### Arguments
85
+
86
+ | Flag | Required | Description |
87
+ |-------------------|----------|--------------------------------------------------------------|
88
+ | `--base` | yes | Full URL of the address_search endpoint |
89
+ | `--input` | batch | Input CSV path |
90
+ | `--address-column`| batch | Column name in the input CSV that holds the address |
91
+ | `--output` | batch | Output CSV path |
92
+ | `--log` | batch | Log file path |
93
+ | `--encoding` | no | Input CSV encoding (e.g. `utf-8`, `gbk`, `gb18030`); default `utf-8` |
94
+ | `--address` | single | Single address to geocode (printed to stdout) |
95
+
96
+ `--address` and the `--input` group are mutually exclusive.
@@ -0,0 +1,70 @@
1
+ # shw-geocoder
2
+
3
+ A minimal PyPI package that wraps the SHSMI `address_search` geocoding API. **THIS PACKAGE IS SPECIFIC TO CERTAIN GEOCODING APIS AND IS NOT UNIVERSAL.**
4
+
5
+ ## Install
6
+
7
+ ```bash
8
+ pip install shw-geocoder
9
+ ```
10
+
11
+ For local development:
12
+
13
+ ```bash
14
+ pip install -e .
15
+ ```
16
+
17
+ ## Python API
18
+
19
+ ```python
20
+ from shw_geocoder import geocode
21
+
22
+ result = geocode("your-address", "geocoder-url")
23
+ print(result["data"][0]["pointX"], result["data"][0]["pointY"])
24
+ ```
25
+
26
+ `geocode(address, base, result_count=3)` returns the raw JSON returned by the
27
+ service. When the address cannot be resolved, the payload's `count` field is `0`
28
+ and `data` is empty.
29
+
30
+ ## CLI
31
+
32
+ The package exposes a `shw-geocoder` command.
33
+
34
+ ### Single address
35
+
36
+ ```bash
37
+ shw-geocoder --base "geocoder-url" \
38
+ --address "your-address"
39
+ ```
40
+
41
+ Prints `x, y, accuracy, poi, room_count` to stdout.
42
+
43
+ ### Batch (CSV in -> CSV out + log)
44
+
45
+ ```bash
46
+ shw-geocoder --base "geocoder-url" \
47
+ --input addrs.csv \
48
+ --address-column address \
49
+ --output out.csv \
50
+ --log run.log
51
+ ```
52
+
53
+ - `out.csv` keeps every original column from `addrs.csv` and appends five new
54
+ columns: `x, y, accuracy, poi, room_count`. Failed rows leave these blank.
55
+ - `run.log` is a plain-text summary containing start/end time, elapsed seconds,
56
+ total / success / failed counts, and a breakdown by `accuracy` value.
57
+
58
+ ### Arguments
59
+
60
+ | Flag | Required | Description |
61
+ |-------------------|----------|--------------------------------------------------------------|
62
+ | `--base` | yes | Full URL of the address_search endpoint |
63
+ | `--input` | batch | Input CSV path |
64
+ | `--address-column`| batch | Column name in the input CSV that holds the address |
65
+ | `--output` | batch | Output CSV path |
66
+ | `--log` | batch | Log file path |
67
+ | `--encoding` | no | Input CSV encoding (e.g. `utf-8`, `gbk`, `gb18030`); default `utf-8` |
68
+ | `--address` | single | Single address to geocode (printed to stdout) |
69
+
70
+ `--address` and the `--input` group are mutually exclusive.
@@ -0,0 +1,36 @@
1
+ [build-system]
2
+ requires = ["setuptools>=61"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "shw-geocoder"
7
+ version = "0.1.0"
8
+ description = "Geocoder client for SHSMI address_search API"
9
+ readme = "README.md"
10
+ requires-python = ">=3.8"
11
+ license = { text = "MIT" }
12
+ authors = [{ name = "zx" }]
13
+ keywords = ["geocoder", "geocoding", "address", "shsmi", "csv"]
14
+ classifiers = [
15
+ "Development Status :: 3 - Alpha",
16
+ "Intended Audience :: Developers",
17
+ "License :: OSI Approved :: MIT License",
18
+ "Operating System :: OS Independent",
19
+ "Programming Language :: Python :: 3",
20
+ "Programming Language :: Python :: 3 :: Only",
21
+ "Programming Language :: Python :: 3.8",
22
+ "Programming Language :: Python :: 3.9",
23
+ "Programming Language :: Python :: 3.10",
24
+ "Programming Language :: Python :: 3.11",
25
+ "Programming Language :: Python :: 3.12",
26
+ "Topic :: Scientific/Engineering :: GIS",
27
+ "Topic :: Utilities",
28
+ ]
29
+ dependencies = ["requests"]
30
+
31
+ [project.scripts]
32
+ shw-geocoder = "shw_geocoder.cli:main"
33
+
34
+ [tool.setuptools.packages.find]
35
+ include = ["shw_geocoder*"]
36
+ exclude = ["tests*"]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,4 @@
1
+ from .geocoder import geocode
2
+
3
+ __version__ = "0.1.0"
4
+ __all__ = ["geocode"]
@@ -0,0 +1,4 @@
1
+ from .cli import main
2
+
3
+ if __name__ == "__main__":
4
+ main()
@@ -0,0 +1,175 @@
1
+ """Command-line entry point for shw-geocoder."""
2
+
3
+ import argparse
4
+ import csv
5
+ import sys
6
+ import time
7
+ from collections import Counter
8
+ from datetime import datetime
9
+
10
+ from .geocoder import geocode
11
+
12
+ NEW_COLUMNS = ["x", "y", "accuracy", "poi", "room_count"]
13
+
14
+
15
+ def _extract(result: dict):
16
+ """Return (x, y, accuracy, poi, room_count) from an API response, or None."""
17
+ if not result or result.get("count", 0) <= 0:
18
+ return None
19
+ data = result.get("data") or []
20
+ if not data:
21
+ return None
22
+ d = data[0]
23
+ return (
24
+ d.get("pointX"),
25
+ d.get("pointY"),
26
+ d.get("resultAccuracy"),
27
+ d.get("poiString"),
28
+ d.get("roomCount"),
29
+ )
30
+
31
+
32
+ def _progress(done: int, total: int):
33
+ if total <= 0:
34
+ return
35
+ pct = done * 100 // total
36
+ sys.stderr.write(f"\r[{done}/{total}] {pct}%")
37
+ sys.stderr.flush()
38
+ if done == total:
39
+ sys.stderr.write("\n")
40
+
41
+
42
+ def _run_single(address: str, base: str) -> int:
43
+ try:
44
+ result = geocode(address, base)
45
+ except Exception as exc: # noqa: BLE001
46
+ print(f"geocode failed: {exc}", file=sys.stderr)
47
+ return 1
48
+ fields = _extract(result)
49
+ if fields is None:
50
+ print("no result", file=sys.stderr)
51
+ return 1
52
+ print("x={}, y={}, accuracy={}, poi={}, room_count={}".format(*fields))
53
+ return 0
54
+
55
+
56
+ def _run_batch(args) -> int:
57
+ start = datetime.now()
58
+ start_ts = time.time()
59
+
60
+ encoding = args.encoding
61
+ print(f"using encoding: {encoding}", file=sys.stderr)
62
+ try:
63
+ with open(args.input, newline="", encoding=encoding) as f:
64
+ reader = csv.DictReader(f)
65
+ fieldnames = list(reader.fieldnames or [])
66
+ rows = list(reader)
67
+ except UnicodeDecodeError as exc:
68
+ print(
69
+ f"error: failed to decode '{args.input}' as '{encoding}' ({exc}). "
70
+ f"The file's actual encoding does not match --encoding. "
71
+ f"Re-run with the correct --encoding (e.g. utf-8, gbk, gb18030).",
72
+ file=sys.stderr,
73
+ )
74
+ return 2
75
+
76
+ if args.address_column not in fieldnames:
77
+ print(
78
+ f"error: address column '{args.address_column}' not found in "
79
+ f"input CSV (columns: {fieldnames})",
80
+ file=sys.stderr,
81
+ )
82
+ return 2
83
+
84
+ total = len(rows)
85
+ success = 0
86
+ fail = 0
87
+ acc_counter = Counter()
88
+
89
+ out_fields = fieldnames + [c for c in NEW_COLUMNS if c not in fieldnames]
90
+
91
+ with open(args.output, "w", newline="", encoding="utf-8") as f:
92
+ writer = csv.DictWriter(f, fieldnames=out_fields)
93
+ writer.writeheader()
94
+ for i, row in enumerate(rows, 1):
95
+ address = row.get(args.address_column) or ""
96
+ try:
97
+ result = geocode(address, args.base)
98
+ except Exception: # noqa: BLE001
99
+ result = None
100
+ fields = _extract(result)
101
+ if fields is None:
102
+ fail += 1
103
+ acc_counter["<nan>"] += 1
104
+ else:
105
+ success += 1
106
+ x, y, acc, poi, room = fields
107
+ row["x"] = x
108
+ row["y"] = y
109
+ row["accuracy"] = acc
110
+ row["poi"] = poi
111
+ row["room_count"] = room
112
+ acc_counter[str(acc)] += 1
113
+ writer.writerow(row)
114
+ _progress(i, total)
115
+
116
+ end = datetime.now()
117
+ elapsed = time.time() - start_ts
118
+ _write_log(args.log, start, end, elapsed, total, success, fail, acc_counter)
119
+ return 0
120
+
121
+
122
+ def _write_log(path, start, end, elapsed, total, success, fail, acc_counter):
123
+ lines = [
124
+ "shw-geocoder run summary",
125
+ "========================",
126
+ f"Start: {start.strftime('%Y-%m-%d %H:%M:%S')}",
127
+ f"End: {end.strftime('%Y-%m-%d %H:%M:%S')}",
128
+ f"Elapsed: {elapsed:.1f} s",
129
+ f"Total: {total}",
130
+ f"Success: {success}",
131
+ f"Failed: {fail}",
132
+ "Accuracy breakdown:",
133
+ ]
134
+ for acc, cnt in sorted(acc_counter.items()):
135
+ lines.append(f" {acc} : {cnt}")
136
+ with open(path, "w", encoding="utf-8") as f:
137
+ f.write("\n".join(lines) + "\n")
138
+
139
+
140
+ def build_parser() -> argparse.ArgumentParser:
141
+ p = argparse.ArgumentParser(prog="shw-geocoder", description=__doc__)
142
+ p.add_argument("--base", required=True, help="Full URL of the address_search endpoint")
143
+
144
+ group = p.add_mutually_exclusive_group(required=True)
145
+ group.add_argument("--input", help="Input CSV path (batch mode)")
146
+ group.add_argument("--address", help="Single address to geocode")
147
+
148
+ p.add_argument("--address-column", help="Column name holding the address in the input CSV")
149
+ p.add_argument("--encoding", default="utf-8", help="Input CSV encoding (e.g. utf-8, gbk, gb18030); default utf-8")
150
+ p.add_argument("--output", help="Output CSV path (batch mode)")
151
+ p.add_argument("--log", help="Log file path (batch mode)")
152
+ return p
153
+
154
+
155
+ def main(argv=None) -> int:
156
+ args = build_parser().parse_args(argv)
157
+
158
+ if args.input:
159
+ missing = []
160
+ if not args.address_column:
161
+ missing.append("--address-column")
162
+ if not args.output:
163
+ missing.append("--output")
164
+ if not args.log:
165
+ missing.append("--log")
166
+ if missing:
167
+ print(f"error: batch mode requires {' '.join(missing)}", file=sys.stderr)
168
+ return 2
169
+ return _run_batch(args)
170
+
171
+ return _run_single(args.address, args.base)
172
+
173
+
174
+ if __name__ == "__main__":
175
+ sys.exit(main())
@@ -0,0 +1,30 @@
1
+ """Thin client around the SHSMI address_search endpoint."""
2
+
3
+ import requests
4
+
5
+
6
+ def geocode(address: str, base: str, result_count: int = 3, timeout: float = 10.0) -> dict:
7
+ """Geocode a single address.
8
+
9
+ Parameters
10
+ ----------
11
+ address:
12
+ The address text to search for.
13
+ base:
14
+ Full URL of the `address_search?parameters` endpoint.
15
+ result_count:
16
+ How many candidate results to request from the service.
17
+ timeout:
18
+ Per-request timeout in seconds.
19
+
20
+ Returns
21
+ -------
22
+ dict
23
+ The raw JSON payload returned by the service. When the address cannot
24
+ be resolved, the payload's ``count`` field is ``0`` and ``data`` is
25
+ empty.
26
+ """
27
+ params = {"search_word": address, "result_count": result_count}
28
+ resp = requests.get(base, params=params, timeout=timeout)
29
+ resp.raise_for_status()
30
+ return resp.json()
@@ -0,0 +1,96 @@
1
+ Metadata-Version: 2.4
2
+ Name: shw-geocoder
3
+ Version: 0.1.0
4
+ Summary: Geocoder client for SHSMI address_search API
5
+ Author: zx
6
+ License: MIT
7
+ Keywords: geocoder,geocoding,address,shsmi,csv
8
+ Classifier: Development Status :: 3 - Alpha
9
+ Classifier: Intended Audience :: Developers
10
+ Classifier: License :: OSI Approved :: MIT License
11
+ Classifier: Operating System :: OS Independent
12
+ Classifier: Programming Language :: Python :: 3
13
+ Classifier: Programming Language :: Python :: 3 :: Only
14
+ Classifier: Programming Language :: Python :: 3.8
15
+ Classifier: Programming Language :: Python :: 3.9
16
+ Classifier: Programming Language :: Python :: 3.10
17
+ Classifier: Programming Language :: Python :: 3.11
18
+ Classifier: Programming Language :: Python :: 3.12
19
+ Classifier: Topic :: Scientific/Engineering :: GIS
20
+ Classifier: Topic :: Utilities
21
+ Requires-Python: >=3.8
22
+ Description-Content-Type: text/markdown
23
+ License-File: LICENSE
24
+ Requires-Dist: requests
25
+ Dynamic: license-file
26
+
27
+ # shw-geocoder
28
+
29
+ A minimal PyPI package that wraps the SHSMI `address_search` geocoding API. **THIS PACKAGE IS SPECIFIC TO CERTAIN GEOCODING APIS AND IS NOT UNIVERSAL.**
30
+
31
+ ## Install
32
+
33
+ ```bash
34
+ pip install shw-geocoder
35
+ ```
36
+
37
+ For local development:
38
+
39
+ ```bash
40
+ pip install -e .
41
+ ```
42
+
43
+ ## Python API
44
+
45
+ ```python
46
+ from shw_geocoder import geocode
47
+
48
+ result = geocode("your-address", "geocoder-url")
49
+ print(result["data"][0]["pointX"], result["data"][0]["pointY"])
50
+ ```
51
+
52
+ `geocode(address, base, result_count=3)` returns the raw JSON returned by the
53
+ service. When the address cannot be resolved, the payload's `count` field is `0`
54
+ and `data` is empty.
55
+
56
+ ## CLI
57
+
58
+ The package exposes a `shw-geocoder` command.
59
+
60
+ ### Single address
61
+
62
+ ```bash
63
+ shw-geocoder --base "geocoder-url" \
64
+ --address "your-address"
65
+ ```
66
+
67
+ Prints `x, y, accuracy, poi, room_count` to stdout.
68
+
69
+ ### Batch (CSV in -> CSV out + log)
70
+
71
+ ```bash
72
+ shw-geocoder --base "geocoder-url" \
73
+ --input addrs.csv \
74
+ --address-column address \
75
+ --output out.csv \
76
+ --log run.log
77
+ ```
78
+
79
+ - `out.csv` keeps every original column from `addrs.csv` and appends five new
80
+ columns: `x, y, accuracy, poi, room_count`. Failed rows leave these blank.
81
+ - `run.log` is a plain-text summary containing start/end time, elapsed seconds,
82
+ total / success / failed counts, and a breakdown by `accuracy` value.
83
+
84
+ ### Arguments
85
+
86
+ | Flag | Required | Description |
87
+ |-------------------|----------|--------------------------------------------------------------|
88
+ | `--base` | yes | Full URL of the address_search endpoint |
89
+ | `--input` | batch | Input CSV path |
90
+ | `--address-column`| batch | Column name in the input CSV that holds the address |
91
+ | `--output` | batch | Output CSV path |
92
+ | `--log` | batch | Log file path |
93
+ | `--encoding` | no | Input CSV encoding (e.g. `utf-8`, `gbk`, `gb18030`); default `utf-8` |
94
+ | `--address` | single | Single address to geocode (printed to stdout) |
95
+
96
+ `--address` and the `--input` group are mutually exclusive.
@@ -0,0 +1,14 @@
1
+ LICENSE
2
+ MANIFEST.in
3
+ README.md
4
+ pyproject.toml
5
+ shw_geocoder/__init__.py
6
+ shw_geocoder/__main__.py
7
+ shw_geocoder/cli.py
8
+ shw_geocoder/geocoder.py
9
+ shw_geocoder.egg-info/PKG-INFO
10
+ shw_geocoder.egg-info/SOURCES.txt
11
+ shw_geocoder.egg-info/dependency_links.txt
12
+ shw_geocoder.egg-info/entry_points.txt
13
+ shw_geocoder.egg-info/requires.txt
14
+ shw_geocoder.egg-info/top_level.txt
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ shw-geocoder = shw_geocoder.cli:main
@@ -0,0 +1 @@
1
+ requests
@@ -0,0 +1 @@
1
+ shw_geocoder