Skip to content

Commit 74d7ee4

Browse files
Merge pull request #6 from Geocodio/fix/batch-reverse-geocoding
Fix batch reverse geocoding coordinate format
2 parents 31a3abc + 979fc6f commit 74d7ee4

File tree

4 files changed

+193
-32
lines changed

4 files changed

+193
-32
lines changed

CHANGELOG.md

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
# Changelog
2+
3+
All notable changes to this project will be documented in this file.
4+
5+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7+
8+
## [Unreleased]
9+
10+
### Added
11+
12+
### Fixed
13+
14+
### Changed
15+
16+
### Deprecated
17+
18+
### Removed
19+
20+
### Security
21+
22+
## Release Process
23+
24+
When ready to release:
25+
1. Update the version in `pyproject.toml`
26+
2. Move all "Unreleased" items to a new version section with date
27+
3. Commit with message: `chore: prepare release vX.Y.Z`
28+
4. Tag the release: `git tag vX.Y.Z`
29+
5. Push tags: `git push --tags`
30+
6. GitHub Actions will automatically publish to PyPI
31+
32+
[Unreleased]: https://github.com/Geocodio/geocodio-library-python/compare/main...HEAD

src/geocodio/client.py

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -124,12 +124,18 @@ def reverse(
124124
params["limit"] = int(limit)
125125

126126
endpoint: str
127-
data: Dict[str, list] | None
127+
data: Union[List[str], None]
128128

129129
# Batch vs single coordinate
130130
if isinstance(coordinate, list):
131131
endpoint = f"{self.BASE_PATH}/reverse"
132-
data = {"coordinates": coordinate}
132+
coords_as_strings = []
133+
for coord in coordinate:
134+
if isinstance(coord, tuple):
135+
coords_as_strings.append(f"{coord[0]},{coord[1]}")
136+
else:
137+
coords_as_strings.append(coord)
138+
data = coords_as_strings
133139
else:
134140
endpoint = f"{self.BASE_PATH}/reverse"
135141
if isinstance(coordinate, tuple):
@@ -158,7 +164,7 @@ def _request(
158164
logger.debug(f"JSON body: {json}")
159165
logger.debug(f"Files: {files}")
160166

161-
resp = self._http.request(method, endpoint, params=params, json=json, files=files, timeout=30)
167+
resp = self._http.request(method, endpoint, params=params, json=json, files=files, timeout=60)
162168

163169
logger.debug(f"Response status code: {resp.status_code}")
164170
logger.debug(f"Response headers: {resp.headers}")

tests/e2e/test_batch_reverse.py

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
"""
2+
End-to-end tests for batch reverse geocoding functionality.
3+
"""
4+
5+
import pytest
6+
from geocodio import GeocodioClient
7+
8+
9+
def test_batch_reverse_geocoding(client):
10+
"""Test batch reverse geocoding against real API."""
11+
# Arrange
12+
coordinates = [
13+
(38.886665, -77.094733), # Arlington, VA
14+
(38.897676, -77.036530), # White House
15+
(37.331669, -122.030090) # Apple Park
16+
]
17+
18+
# Act
19+
response = client.reverse(coordinates)
20+
21+
# Assert
22+
assert response is not None
23+
assert len(response.results) == 3
24+
25+
# Check first result (Arlington, VA)
26+
arlington = response.results[0]
27+
assert "Arlington" in arlington.formatted_address
28+
assert "VA" in arlington.formatted_address
29+
assert arlington.location.lat == pytest.approx(38.886672, abs=0.001)
30+
assert arlington.location.lng == pytest.approx(-77.094735, abs=0.001)
31+
32+
# Check second result (White House)
33+
white_house = response.results[1]
34+
assert "Pennsylvania" in white_house.formatted_address
35+
assert "Washington" in white_house.formatted_address or "DC" in white_house.formatted_address
36+
37+
# Check third result (Apple Park)
38+
apple_park = response.results[2]
39+
assert "Cupertino" in apple_park.formatted_address or "CA" in apple_park.formatted_address
40+
41+
42+
def test_batch_reverse_with_strings(client):
43+
"""Test batch reverse geocoding with string coordinates."""
44+
# Arrange
45+
coordinates = [
46+
"38.886665,-77.094733", # Arlington, VA
47+
"38.897676,-77.036530" # White House
48+
]
49+
50+
# Act
51+
response = client.reverse(coordinates)
52+
53+
# Assert
54+
assert response is not None
55+
assert len(response.results) == 2
56+
assert "Arlington" in response.results[0].formatted_address
57+
assert "Pennsylvania" in response.results[1].formatted_address or "Washington" in response.results[1].formatted_address
58+
59+
60+
def test_batch_reverse_with_fields(client):
61+
"""Test batch reverse geocoding with additional fields."""
62+
# Arrange
63+
coordinates = [
64+
(38.886665, -77.094733), # Arlington, VA
65+
(38.897676, -77.036530) # White House
66+
]
67+
68+
# Act
69+
response = client.reverse(coordinates, fields=["timezone", "cd"])
70+
71+
# Assert
72+
assert response is not None
73+
assert len(response.results) == 2
74+
75+
# Check that fields are populated
76+
for result in response.results:
77+
assert result.fields is not None
78+
if result.fields.timezone:
79+
assert result.fields.timezone.name is not None
80+
if result.fields.congressional_districts:
81+
assert len(result.fields.congressional_districts) > 0
82+
83+
84+
def test_empty_batch_reverse(client):
85+
"""Test batch reverse geocoding with empty list."""
86+
# Arrange
87+
coordinates = []
88+
89+
# Act & Assert
90+
with pytest.raises(Exception):
91+
client.reverse(coordinates)
92+
93+
94+
def test_mixed_batch_reverse_formats(client):
95+
"""Test batch reverse geocoding with mixed coordinate formats."""
96+
# Note: The API expects consistent format, so this tests error handling
97+
# Arrange
98+
coordinates = [
99+
(38.886665, -77.094733), # Tuple format
100+
"38.897676,-77.036530" # String format
101+
]
102+
103+
# Act
104+
# The library should handle converting these to a consistent format
105+
response = client.reverse(coordinates)
106+
107+
# Assert
108+
assert response is not None
109+
assert len(response.results) == 2

tests/unit/test_reverse.py

Lines changed: 43 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -58,37 +58,51 @@ def batch_response_callback(request):
5858
return httpx.Response(200, json={
5959
"results": [
6060
{
61-
"address_components": {
62-
"number": "1109",
63-
"predirectional": "N",
64-
"street": "Highland",
65-
"suffix": "St",
66-
"formatted_street": "N Highland St",
67-
"city": "Arlington",
68-
"state": "VA",
69-
"zip": "22201"
70-
},
71-
"formatted_address": "1109 N Highland St, Arlington, VA 22201",
72-
"location": {"lat": 38.886672, "lng": -77.094735},
73-
"accuracy": 1,
74-
"accuracy_type": "rooftop",
75-
"source": "Arlington"
61+
"query": "38.886672,-77.094735",
62+
"response": {
63+
"results": [
64+
{
65+
"address_components": {
66+
"number": "1109",
67+
"predirectional": "N",
68+
"street": "Highland",
69+
"suffix": "St",
70+
"formatted_street": "N Highland St",
71+
"city": "Arlington",
72+
"state": "VA",
73+
"zip": "22201"
74+
},
75+
"formatted_address": "1109 N Highland St, Arlington, VA 22201",
76+
"location": {"lat": 38.886672, "lng": -77.094735},
77+
"accuracy": 1,
78+
"accuracy_type": "rooftop",
79+
"source": "Arlington"
80+
}
81+
]
82+
}
7683
},
7784
{
78-
"address_components": {
79-
"number": "1600",
80-
"street": "Pennsylvania",
81-
"suffix": "Ave",
82-
"postdirectional": "NW",
83-
"city": "Washington",
84-
"state": "DC",
85-
"zip": "20500"
86-
},
87-
"formatted_address": "1600 Pennsylvania Ave NW, Washington, DC 20500",
88-
"location": {"lat": 38.898719, "lng": -77.036547},
89-
"accuracy": 1,
90-
"accuracy_type": "rooftop",
91-
"source": "DC"
85+
"query": "38.898719,-77.036547",
86+
"response": {
87+
"results": [
88+
{
89+
"address_components": {
90+
"number": "1600",
91+
"street": "Pennsylvania",
92+
"suffix": "Ave",
93+
"postdirectional": "NW",
94+
"city": "Washington",
95+
"state": "DC",
96+
"zip": "20500"
97+
},
98+
"formatted_address": "1600 Pennsylvania Ave NW, Washington, DC 20500",
99+
"location": {"lat": 38.898719, "lng": -77.036547},
100+
"accuracy": 1,
101+
"accuracy_type": "rooftop",
102+
"source": "DC"
103+
}
104+
]
105+
}
92106
}
93107
]
94108
})

0 commit comments

Comments
 (0)