geocodio-gem 0.3.0 → 0.5.0
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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +16 -0
- data/Gemfile.lock +1 -1
- data/README.md +227 -0
- data/lib/geocodio/gem/version.rb +1 -1
- data/lib/geocodio/gem.rb +341 -6
- metadata +2 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 0dcf45fbaa749a3272901c9448961eadc96adcd24f8d1122ab9ae691e76ad30e
|
|
4
|
+
data.tar.gz: 3f26bbc4c74fe7b9062d952615d7de520d4ef3043d41d0bc3cfb02fa5def5f97
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: fc0809146e16875b323b13da38765f1a2267238f6956dc9f0a04deca4a01f6a50ea427419d6ef6a123009303ab58e3d59f37329e4e80baea943dde16a5ed2582
|
|
7
|
+
data.tar.gz: d04e79756c2c7fb4682f26d928f40239634f0d20782df767356021d69b9afcf0622ad7fb833774c0f88a59b85f93823bef13b4d35dca6dde835236960c21ccec
|
data/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,19 @@
|
|
|
1
|
+
## [0.5.0] - 2026-01-06
|
|
2
|
+
- Added Distance API support:
|
|
3
|
+
- `distance()` - Calculate distances from single origin to multiple destinations
|
|
4
|
+
- `distanceMatrix()` - Calculate full distance matrix (multiple origins x destinations)
|
|
5
|
+
- `createDistanceMatrixJob()` - Create async distance matrix jobs
|
|
6
|
+
- `distanceMatrixJobStatus()` - Check job status
|
|
7
|
+
- `distanceMatrixJobs()` - List all distance jobs
|
|
8
|
+
- `getDistanceMatrixJobResults()` - Get completed job results
|
|
9
|
+
- `downloadDistanceMatrixJob()` - Download results to file
|
|
10
|
+
- `deleteDistanceMatrixJob()` - Delete a job
|
|
11
|
+
- Enhanced `geocode()` and `reverse()` with optional distance parameters
|
|
12
|
+
- Support for multiple coordinate formats (string, array, hash) with optional IDs
|
|
13
|
+
- Support for driving mode with duration estimates
|
|
14
|
+
- Support for distance filtering options (max_results, max_distance, etc.)
|
|
15
|
+
- Updated API endpoint from v1.8 to v1.9
|
|
16
|
+
|
|
1
17
|
## [0.3.0] - 2025-5-20
|
|
2
18
|
- Updated API endpoint from v1.7 to v1.8
|
|
3
19
|
|
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
|
@@ -213,6 +213,233 @@ To use `deleteList()`, pass in the ID of the list you would like to delete.
|
|
|
213
213
|
response # => {"success"=>true}
|
|
214
214
|
```
|
|
215
215
|
|
|
216
|
+
## Distance Calculations
|
|
217
|
+
|
|
218
|
+
The Distance API allows you to calculate distances from a single origin to multiple destinations, or compute full distance matrices.
|
|
219
|
+
|
|
220
|
+
### Coordinate Formats with Custom IDs
|
|
221
|
+
|
|
222
|
+
You can add custom identifiers to coordinates using various formats. The ID will be returned in the response, making it easy to match results back to your data:
|
|
223
|
+
|
|
224
|
+
```ruby
|
|
225
|
+
# String format with ID
|
|
226
|
+
"37.7749,-122.4194,warehouse_1"
|
|
227
|
+
|
|
228
|
+
# Array format with ID
|
|
229
|
+
[37.7749, -122.4194, "warehouse_1"]
|
|
230
|
+
|
|
231
|
+
# Hash format with ID
|
|
232
|
+
{ lat: 37.7749, lng: -122.4194, id: "warehouse_1" }
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
### Distance Mode and Units
|
|
236
|
+
|
|
237
|
+
The SDK supports different distance calculation modes and units:
|
|
238
|
+
|
|
239
|
+
```ruby
|
|
240
|
+
# Available modes
|
|
241
|
+
:straightline # Default - great-circle (as the crow flies)
|
|
242
|
+
:driving # Road network routing with duration
|
|
243
|
+
:haversine # Alias for straightline (backward compatibility)
|
|
244
|
+
|
|
245
|
+
# Available units
|
|
246
|
+
:miles # Default
|
|
247
|
+
:kilometers
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
> **Note:** The default mode is `:straightline` (great-circle distance). Use `:driving` if you need road network routing with duration estimates.
|
|
251
|
+
|
|
252
|
+
### Single Origin to Multiple Destinations
|
|
253
|
+
|
|
254
|
+
Calculate distances from one origin to multiple destinations:
|
|
255
|
+
|
|
256
|
+
```ruby
|
|
257
|
+
# Basic usage with string coordinates
|
|
258
|
+
response = geocodio.distance(
|
|
259
|
+
"38.8977,-77.0365,white_house",
|
|
260
|
+
["38.9072,-77.0369,capitol", "38.8895,-77.0353,monument"]
|
|
261
|
+
)
|
|
262
|
+
|
|
263
|
+
response["origin"]["id"] # => "white_house"
|
|
264
|
+
response["destinations"][0]["distance_miles"] # => 0.7
|
|
265
|
+
response["destinations"][0]["id"] # => "capitol"
|
|
266
|
+
|
|
267
|
+
# With driving mode (includes duration)
|
|
268
|
+
response = geocodio.distance(
|
|
269
|
+
"38.8977,-77.0365",
|
|
270
|
+
["38.9072,-77.0369"],
|
|
271
|
+
mode: :driving
|
|
272
|
+
)
|
|
273
|
+
|
|
274
|
+
response["destinations"][0]["duration_seconds"] # => 180
|
|
275
|
+
|
|
276
|
+
# With filtering and sorting options
|
|
277
|
+
response = geocodio.distance(
|
|
278
|
+
"38.8977,-77.0365,warehouse",
|
|
279
|
+
[
|
|
280
|
+
"38.9072,-77.0369,store_1",
|
|
281
|
+
"39.2904,-76.6122,store_2",
|
|
282
|
+
"39.9526,-75.1652,store_3"
|
|
283
|
+
],
|
|
284
|
+
mode: :driving,
|
|
285
|
+
units: :kilometers,
|
|
286
|
+
max_results: 2,
|
|
287
|
+
max_distance: 100.0,
|
|
288
|
+
order_by: :distance,
|
|
289
|
+
sort: :asc
|
|
290
|
+
)
|
|
291
|
+
|
|
292
|
+
# Using array format for coordinates
|
|
293
|
+
response = geocodio.distance(
|
|
294
|
+
[38.8977, -77.0365, "headquarters"],
|
|
295
|
+
[[38.9072, -77.0369, "branch_1"]]
|
|
296
|
+
)
|
|
297
|
+
|
|
298
|
+
# Using hash format for coordinates
|
|
299
|
+
response = geocodio.distance(
|
|
300
|
+
{ lat: 38.8977, lng: -77.0365, id: "hq" },
|
|
301
|
+
[{ lat: 38.9072, lng: -77.0369, id: "branch" }]
|
|
302
|
+
)
|
|
303
|
+
```
|
|
304
|
+
|
|
305
|
+
### Distance Matrix (Multiple Origins to Multiple Destinations)
|
|
306
|
+
|
|
307
|
+
Calculate full distance matrix from multiple origins to multiple destinations:
|
|
308
|
+
|
|
309
|
+
```ruby
|
|
310
|
+
origins = [
|
|
311
|
+
"38.8977,-77.0365,warehouse_dc",
|
|
312
|
+
"39.2904,-76.6122,warehouse_baltimore"
|
|
313
|
+
]
|
|
314
|
+
destinations = [
|
|
315
|
+
"38.9072,-77.0369,customer_1",
|
|
316
|
+
"39.9526,-75.1652,customer_2"
|
|
317
|
+
]
|
|
318
|
+
|
|
319
|
+
response = geocodio.distanceMatrix(origins, destinations)
|
|
320
|
+
|
|
321
|
+
response["results"][0]["origin"]["id"] # => "warehouse_dc"
|
|
322
|
+
response["results"][0]["destinations"][0]["distance_miles"] # => 0.7
|
|
323
|
+
|
|
324
|
+
# With driving mode
|
|
325
|
+
response = geocodio.distanceMatrix(
|
|
326
|
+
origins,
|
|
327
|
+
destinations,
|
|
328
|
+
mode: :driving,
|
|
329
|
+
units: :kilometers
|
|
330
|
+
)
|
|
331
|
+
|
|
332
|
+
# With filtering options
|
|
333
|
+
response = geocodio.distanceMatrix(
|
|
334
|
+
origins,
|
|
335
|
+
destinations,
|
|
336
|
+
max_results: 2,
|
|
337
|
+
max_distance: 50.0,
|
|
338
|
+
min_distance: 1.0,
|
|
339
|
+
order_by: :distance,
|
|
340
|
+
sort: :asc
|
|
341
|
+
)
|
|
342
|
+
```
|
|
343
|
+
|
|
344
|
+
### Add Distance to Geocoding Requests
|
|
345
|
+
|
|
346
|
+
You can add distance calculations to existing geocode or reverse geocode requests:
|
|
347
|
+
|
|
348
|
+
```ruby
|
|
349
|
+
# Geocode an address and calculate distances to store locations
|
|
350
|
+
response = geocodio.geocode(
|
|
351
|
+
["1600 Pennsylvania Ave NW, Washington DC"],
|
|
352
|
+
[], # fields
|
|
353
|
+
nil, # limit
|
|
354
|
+
nil, # format
|
|
355
|
+
destinations: [
|
|
356
|
+
"38.9072,-77.0369,store_dc",
|
|
357
|
+
"39.2904,-76.6122,store_baltimore"
|
|
358
|
+
],
|
|
359
|
+
distance_mode: :driving,
|
|
360
|
+
distance_units: :miles
|
|
361
|
+
)
|
|
362
|
+
|
|
363
|
+
response["results"][0]["destinations"][0]["distance_miles"] # => distance to first destination
|
|
364
|
+
response["results"][0]["destinations"][0]["id"] # => "store_dc"
|
|
365
|
+
|
|
366
|
+
# Reverse geocode with distances
|
|
367
|
+
response = geocodio.reverse(
|
|
368
|
+
["38.8977,-77.0365"],
|
|
369
|
+
[],
|
|
370
|
+
nil,
|
|
371
|
+
nil,
|
|
372
|
+
destinations: ["38.9072,-77.0369,capitol"],
|
|
373
|
+
distance_mode: :straightline
|
|
374
|
+
)
|
|
375
|
+
|
|
376
|
+
response["results"][0]["destinations"] # => array of destinations with distances
|
|
377
|
+
```
|
|
378
|
+
|
|
379
|
+
### Async Distance Matrix Jobs
|
|
380
|
+
|
|
381
|
+
For large distance matrix calculations, use async jobs that process in the background:
|
|
382
|
+
|
|
383
|
+
```ruby
|
|
384
|
+
# Create a new distance matrix job
|
|
385
|
+
job = geocodio.createDistanceMatrixJob(
|
|
386
|
+
"My Distance Calculation",
|
|
387
|
+
["38.8977,-77.0365,origin_1", "38.9072,-77.0369,origin_2"],
|
|
388
|
+
["38.8895,-77.0353,dest_1", "39.2904,-76.6122,dest_2"],
|
|
389
|
+
mode: :driving,
|
|
390
|
+
units: :miles,
|
|
391
|
+
callback_url: "https://example.com/webhook" # Optional
|
|
392
|
+
)
|
|
393
|
+
|
|
394
|
+
job["id"] # => Job identifier
|
|
395
|
+
|
|
396
|
+
# Or use list IDs from previously uploaded lists
|
|
397
|
+
job = geocodio.createDistanceMatrixJob(
|
|
398
|
+
"Distance from Lists",
|
|
399
|
+
12345, # List ID for origins
|
|
400
|
+
67890, # List ID for destinations
|
|
401
|
+
mode: :straightline
|
|
402
|
+
)
|
|
403
|
+
|
|
404
|
+
# Check job status
|
|
405
|
+
status = geocodio.distanceMatrixJobStatus(job["id"])
|
|
406
|
+
|
|
407
|
+
status["data"]["status"] # => "COMPLETED", "PROCESSING", or "FAILED"
|
|
408
|
+
status["data"]["progress"] # => 0-100
|
|
409
|
+
|
|
410
|
+
# List all jobs (paginated)
|
|
411
|
+
jobs = geocodio.distanceMatrixJobs
|
|
412
|
+
jobs = geocodio.distanceMatrixJobs(2) # Page 2
|
|
413
|
+
|
|
414
|
+
# Get job results as parsed hash
|
|
415
|
+
results = geocodio.getDistanceMatrixJobResults(job["id"])
|
|
416
|
+
|
|
417
|
+
results["results"][0]["origin"]["id"] # => "origin_1"
|
|
418
|
+
results["results"][0]["destinations"] # => array of destinations with distances
|
|
419
|
+
|
|
420
|
+
# Download results to file
|
|
421
|
+
geocodio.downloadDistanceMatrixJob(job["id"], "/path/to/results.json")
|
|
422
|
+
|
|
423
|
+
# Delete a job
|
|
424
|
+
geocodio.deleteDistanceMatrixJob(job["id"])
|
|
425
|
+
```
|
|
426
|
+
|
|
427
|
+
### Distance Filtering Options
|
|
428
|
+
|
|
429
|
+
All distance methods support the following filtering options:
|
|
430
|
+
|
|
431
|
+
| Option | Type | Description |
|
|
432
|
+
|--------|------|-------------|
|
|
433
|
+
| `mode` | Symbol | `:straightline` (default), `:driving`, or `:haversine` |
|
|
434
|
+
| `units` | Symbol | `:miles` (default), `:kilometers`, or `:km` |
|
|
435
|
+
| `max_results` | Integer | Limit number of results |
|
|
436
|
+
| `max_distance` | Float | Filter by maximum distance |
|
|
437
|
+
| `min_distance` | Float | Filter by minimum distance |
|
|
438
|
+
| `max_duration` | Integer | Filter by max duration in seconds (driving mode only) |
|
|
439
|
+
| `min_duration` | Integer | Filter by min duration in seconds (driving mode only) |
|
|
440
|
+
| `order_by` | Symbol | `:distance` (default) or `:duration` |
|
|
441
|
+
| `sort` | Symbol | `:asc` (default) or `:desc` |
|
|
442
|
+
|
|
216
443
|
## Testing
|
|
217
444
|
|
|
218
445
|
To run tests, be sure to create a `.env` file that export your Geocodio API Key within a variable of API_KEY.
|
data/lib/geocodio/gem/version.rb
CHANGED
data/lib/geocodio/gem.rb
CHANGED
|
@@ -2,6 +2,7 @@ require_relative "gem/version"
|
|
|
2
2
|
require "faraday"
|
|
3
3
|
require "faraday/follow_redirects"
|
|
4
4
|
require "csv"
|
|
5
|
+
require "uri"
|
|
5
6
|
|
|
6
7
|
module Geocodio
|
|
7
8
|
|
|
@@ -14,7 +15,7 @@ module Geocodio
|
|
|
14
15
|
@api_key = api_key
|
|
15
16
|
|
|
16
17
|
@conn = Faraday.new(
|
|
17
|
-
url: 'https://api.geocod.io/v1.
|
|
18
|
+
url: 'https://api.geocod.io/v1.9/',
|
|
18
19
|
headers: {'Content-Type' => 'application/json' }
|
|
19
20
|
) do |f|
|
|
20
21
|
f.response :follow_redirects
|
|
@@ -22,11 +23,34 @@ module Geocodio
|
|
|
22
23
|
end
|
|
23
24
|
end
|
|
24
25
|
|
|
25
|
-
|
|
26
|
+
# Forward geocode addresses to coordinates
|
|
27
|
+
# @param query [Array] Array of address strings
|
|
28
|
+
# @param fields [Array] Optional fields to append (e.g., ["timezone", "cd"])
|
|
29
|
+
# @param limit [Integer] Maximum number of results
|
|
30
|
+
# @param format [String] Response format ("simple" for simplified response)
|
|
31
|
+
# @param destinations [Array] Optional array of destination coordinates for distance calculation
|
|
32
|
+
# @param distance_mode [Symbol] Distance mode (:straightline, :driving, :haversine)
|
|
33
|
+
# @param distance_units [Symbol] Distance units (:miles, :kilometers)
|
|
34
|
+
# @param distance_options [Hash] Additional distance options (max_results, max_distance, etc.)
|
|
35
|
+
def geocode(query=[], fields=[], limit=nil, format=nil,
|
|
36
|
+
destinations: nil, distance_mode: nil, distance_units: nil, distance_options: nil)
|
|
26
37
|
if query.size < 1
|
|
27
38
|
raise ArgumentError, 'Please provide at least one address to geocode.'
|
|
28
|
-
elsif query.size == 1
|
|
29
|
-
|
|
39
|
+
elsif query.size == 1
|
|
40
|
+
# Build query string manually to handle array parameters correctly
|
|
41
|
+
query_parts = [
|
|
42
|
+
"api_key=#{@api_key}",
|
|
43
|
+
"q=#{URI.encode_www_form_component(query.join(""))}"
|
|
44
|
+
]
|
|
45
|
+
query_parts << "fields=#{URI.encode_www_form_component(fields.join(","))}" if fields && !fields.empty?
|
|
46
|
+
query_parts << "limit=#{limit}" if limit
|
|
47
|
+
query_parts << "format=#{format}" if format
|
|
48
|
+
|
|
49
|
+
# Add distance parameters if destinations provided
|
|
50
|
+
distance_parts = build_geocode_distance_query_parts(destinations, distance_mode, distance_units, distance_options)
|
|
51
|
+
query_parts.concat(distance_parts)
|
|
52
|
+
|
|
53
|
+
response = JSON.parse(@conn.get("geocode?#{query_parts.join('&')}").body)
|
|
30
54
|
return response
|
|
31
55
|
elsif query.size > 1
|
|
32
56
|
response = @conn.post('geocode') do |req|
|
|
@@ -39,11 +63,34 @@ module Geocodio
|
|
|
39
63
|
end
|
|
40
64
|
end
|
|
41
65
|
|
|
42
|
-
|
|
66
|
+
# Reverse geocode coordinates to addresses
|
|
67
|
+
# @param query [Array] Array of coordinate strings ("lat,lng")
|
|
68
|
+
# @param fields [Array] Optional fields to append (e.g., ["timezone", "cd"])
|
|
69
|
+
# @param limit [Integer] Maximum number of results
|
|
70
|
+
# @param format [String] Response format ("simple" for simplified response)
|
|
71
|
+
# @param destinations [Array] Optional array of destination coordinates for distance calculation
|
|
72
|
+
# @param distance_mode [Symbol] Distance mode (:straightline, :driving, :haversine)
|
|
73
|
+
# @param distance_units [Symbol] Distance units (:miles, :kilometers)
|
|
74
|
+
# @param distance_options [Hash] Additional distance options (max_results, max_distance, etc.)
|
|
75
|
+
def reverse(query=[], fields=[], limit=nil, format=nil,
|
|
76
|
+
destinations: nil, distance_mode: nil, distance_units: nil, distance_options: nil)
|
|
43
77
|
if query.size < 1
|
|
44
78
|
raise ArgumentError, 'Please provide at least one set of coordinates to geocode.'
|
|
45
79
|
elsif query.size == 1
|
|
46
|
-
|
|
80
|
+
# Build query string manually to handle array parameters correctly
|
|
81
|
+
query_parts = [
|
|
82
|
+
"api_key=#{@api_key}",
|
|
83
|
+
"q=#{URI.encode_www_form_component(query.join(""))}"
|
|
84
|
+
]
|
|
85
|
+
query_parts << "fields=#{URI.encode_www_form_component(fields.join(","))}" if fields && !fields.empty?
|
|
86
|
+
query_parts << "limit=#{limit}" if limit
|
|
87
|
+
query_parts << "format=#{format}" if format
|
|
88
|
+
|
|
89
|
+
# Add distance parameters if destinations provided
|
|
90
|
+
distance_parts = build_geocode_distance_query_parts(destinations, distance_mode, distance_units, distance_options)
|
|
91
|
+
query_parts.concat(distance_parts)
|
|
92
|
+
|
|
93
|
+
response = JSON.parse(@conn.get("reverse?#{query_parts.join('&')}").body)
|
|
47
94
|
return response
|
|
48
95
|
elsif query.size > 1
|
|
49
96
|
response = @conn.post('reverse') do |req|
|
|
@@ -96,6 +143,294 @@ module Geocodio
|
|
|
96
143
|
response = JSON.parse(@conn.delete("lists/#{id}", { api_key: @api_key}).body)
|
|
97
144
|
return response
|
|
98
145
|
end
|
|
146
|
+
|
|
147
|
+
# Distance API Methods
|
|
148
|
+
|
|
149
|
+
# Calculate distances from a single origin to multiple destinations
|
|
150
|
+
# @param origin [String, Array, Hash] Origin coordinate ("lat,lng" or "lat,lng,id" or [lat, lng] or [lat, lng, id] or {lat:, lng:, id:})
|
|
151
|
+
# @param destinations [Array] Array of destination coordinates in any supported format
|
|
152
|
+
# @param options [Hash] Optional parameters:
|
|
153
|
+
# - mode: :straightline (default), :driving, or :haversine (alias for straightline)
|
|
154
|
+
# - units: :miles (default) or :kilometers
|
|
155
|
+
# - max_results: Integer, limit number of results
|
|
156
|
+
# - max_distance: Float, filter by max distance
|
|
157
|
+
# - max_duration: Integer, filter by max duration (seconds, driving only)
|
|
158
|
+
# - min_distance: Float, filter by min distance
|
|
159
|
+
# - min_duration: Integer, filter by min duration (seconds, driving only)
|
|
160
|
+
# - order_by: :distance (default) or :duration
|
|
161
|
+
# - sort: :asc (default) or :desc
|
|
162
|
+
def distance(origin, destinations, options = {})
|
|
163
|
+
raise ArgumentError, 'Please provide an origin coordinate.' if origin.nil?
|
|
164
|
+
raise ArgumentError, 'Please provide at least one destination.' if destinations.nil? || destinations.empty?
|
|
165
|
+
|
|
166
|
+
# Build query string manually to handle array parameters correctly
|
|
167
|
+
query_parts = [
|
|
168
|
+
"api_key=#{@api_key}",
|
|
169
|
+
"origin=#{URI.encode_www_form_component(format_coordinate_string(origin))}"
|
|
170
|
+
]
|
|
171
|
+
|
|
172
|
+
# Add destinations as properly formatted array parameters
|
|
173
|
+
destinations.each do |dest|
|
|
174
|
+
query_parts << "destinations[]=#{URI.encode_www_form_component(format_coordinate_string(dest))}"
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
# Add distance options
|
|
178
|
+
build_distance_params(options).each do |key, value|
|
|
179
|
+
query_parts << "#{key}=#{URI.encode_www_form_component(value.to_s)}"
|
|
180
|
+
end
|
|
181
|
+
|
|
182
|
+
response = JSON.parse(@conn.get("distance?#{query_parts.join('&')}").body)
|
|
183
|
+
return response
|
|
184
|
+
end
|
|
185
|
+
|
|
186
|
+
# Calculate distance matrix from multiple origins to multiple destinations
|
|
187
|
+
# @param origins [Array] Array of origin coordinates
|
|
188
|
+
# @param destinations [Array] Array of destination coordinates
|
|
189
|
+
# @param options [Hash] Same options as distance() method
|
|
190
|
+
def distanceMatrix(origins, destinations, options = {})
|
|
191
|
+
raise ArgumentError, 'Please provide at least one origin.' if origins.nil? || origins.empty?
|
|
192
|
+
raise ArgumentError, 'Please provide at least one destination.' if destinations.nil? || destinations.empty?
|
|
193
|
+
|
|
194
|
+
body = {
|
|
195
|
+
origins: origins.map { |coord| format_coordinate_object(coord) },
|
|
196
|
+
destinations: destinations.map { |coord| format_coordinate_object(coord) }
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
# Add distance options to body
|
|
200
|
+
body.merge!(build_distance_body_params(options))
|
|
201
|
+
|
|
202
|
+
response = @conn.post('distance-matrix') do |req|
|
|
203
|
+
req.params = { api_key: @api_key }
|
|
204
|
+
req.headers['Content-Type'] = 'application/json'
|
|
205
|
+
req.body = body.to_json
|
|
206
|
+
end
|
|
207
|
+
|
|
208
|
+
parsed = JSON.parse(response.body)
|
|
209
|
+
return parsed
|
|
210
|
+
end
|
|
211
|
+
|
|
212
|
+
# Create an async distance matrix job
|
|
213
|
+
# @param name [String] Job name
|
|
214
|
+
# @param origins [Array, Integer] Array of coordinates or list ID
|
|
215
|
+
# @param destinations [Array, Integer] Array of coordinates or list ID
|
|
216
|
+
# @param options [Hash] Same options as distanceMatrix() plus:
|
|
217
|
+
# - callback_url: URL for webhook notification
|
|
218
|
+
def createDistanceMatrixJob(name, origins, destinations, options = {})
|
|
219
|
+
raise ArgumentError, 'Please provide a job name.' if name.nil? || name.empty?
|
|
220
|
+
raise ArgumentError, 'Please provide origins.' if origins.nil?
|
|
221
|
+
raise ArgumentError, 'Please provide destinations.' if destinations.nil?
|
|
222
|
+
|
|
223
|
+
body = { name: name }
|
|
224
|
+
|
|
225
|
+
# Origins can be array of coordinates or list ID (integer)
|
|
226
|
+
if origins.is_a?(Integer)
|
|
227
|
+
body[:origins] = origins
|
|
228
|
+
else
|
|
229
|
+
body[:origins] = origins.map { |coord| format_coordinate_object(coord) }
|
|
230
|
+
end
|
|
231
|
+
|
|
232
|
+
# Destinations can be array of coordinates or list ID (integer)
|
|
233
|
+
if destinations.is_a?(Integer)
|
|
234
|
+
body[:destinations] = destinations
|
|
235
|
+
else
|
|
236
|
+
body[:destinations] = destinations.map { |coord| format_coordinate_object(coord) }
|
|
237
|
+
end
|
|
238
|
+
|
|
239
|
+
# Add distance options
|
|
240
|
+
body.merge!(build_distance_body_params(options))
|
|
241
|
+
|
|
242
|
+
# Add callback URL if provided
|
|
243
|
+
body[:callback_url] = options[:callback_url] if options[:callback_url]
|
|
244
|
+
|
|
245
|
+
response = @conn.post('distance-jobs') do |req|
|
|
246
|
+
req.params = { api_key: @api_key }
|
|
247
|
+
req.headers['Content-Type'] = 'application/json'
|
|
248
|
+
req.body = body.to_json
|
|
249
|
+
end
|
|
250
|
+
|
|
251
|
+
parsed = JSON.parse(response.body)
|
|
252
|
+
return parsed
|
|
253
|
+
end
|
|
254
|
+
|
|
255
|
+
# Get status of a distance matrix job
|
|
256
|
+
# @param id [String, Integer] Job ID
|
|
257
|
+
def distanceMatrixJobStatus(id)
|
|
258
|
+
response = JSON.parse(@conn.get("distance-jobs/#{id}", { api_key: @api_key }).body)
|
|
259
|
+
return response
|
|
260
|
+
end
|
|
261
|
+
|
|
262
|
+
# List all distance matrix jobs
|
|
263
|
+
# @param page [Integer] Optional page number for pagination
|
|
264
|
+
def distanceMatrixJobs(page = nil)
|
|
265
|
+
params = { api_key: @api_key }
|
|
266
|
+
params[:page] = page if page
|
|
267
|
+
response = JSON.parse(@conn.get("distance-jobs", params).body)
|
|
268
|
+
return response
|
|
269
|
+
end
|
|
270
|
+
|
|
271
|
+
# Get results of a completed distance matrix job
|
|
272
|
+
# @param id [String, Integer] Job ID
|
|
273
|
+
def getDistanceMatrixJobResults(id)
|
|
274
|
+
response = JSON.parse(@conn.get("distance-jobs/#{id}/download", { api_key: @api_key }).body)
|
|
275
|
+
return response
|
|
276
|
+
end
|
|
277
|
+
|
|
278
|
+
# Download distance matrix job results to a file
|
|
279
|
+
# @param id [String, Integer] Job ID
|
|
280
|
+
# @param file_path [String] Path to save the file
|
|
281
|
+
def downloadDistanceMatrixJob(id, file_path)
|
|
282
|
+
response = @conn.get("distance-jobs/#{id}/download", { api_key: @api_key })
|
|
283
|
+
File.write(file_path, response.body)
|
|
284
|
+
return { success: true, file_path: file_path }
|
|
285
|
+
end
|
|
286
|
+
|
|
287
|
+
# Delete a distance matrix job
|
|
288
|
+
# @param id [String, Integer] Job ID
|
|
289
|
+
def deleteDistanceMatrixJob(id)
|
|
290
|
+
response = JSON.parse(@conn.delete("distance-jobs/#{id}", { api_key: @api_key }).body)
|
|
291
|
+
return response
|
|
292
|
+
end
|
|
293
|
+
|
|
294
|
+
private
|
|
295
|
+
|
|
296
|
+
# Format a coordinate for GET requests (string format: "lat,lng" or "lat,lng,id")
|
|
297
|
+
def format_coordinate_string(coord)
|
|
298
|
+
case coord
|
|
299
|
+
when String
|
|
300
|
+
coord
|
|
301
|
+
when Array
|
|
302
|
+
if coord.length >= 3
|
|
303
|
+
"#{coord[0]},#{coord[1]},#{coord[2]}"
|
|
304
|
+
else
|
|
305
|
+
"#{coord[0]},#{coord[1]}"
|
|
306
|
+
end
|
|
307
|
+
when Hash
|
|
308
|
+
str = "#{coord[:lat]},#{coord[:lng]}"
|
|
309
|
+
str += ",#{coord[:id]}" if coord[:id]
|
|
310
|
+
str
|
|
311
|
+
else
|
|
312
|
+
raise ArgumentError, "Invalid coordinate format: #{coord.inspect}"
|
|
313
|
+
end
|
|
314
|
+
end
|
|
315
|
+
|
|
316
|
+
# Format a coordinate for POST requests (object format: {lat:, lng:, id:})
|
|
317
|
+
def format_coordinate_object(coord)
|
|
318
|
+
case coord
|
|
319
|
+
when String
|
|
320
|
+
parts = coord.split(',').map(&:strip)
|
|
321
|
+
obj = { lat: parts[0].to_f, lng: parts[1].to_f }
|
|
322
|
+
obj[:id] = parts[2] if parts[2]
|
|
323
|
+
obj
|
|
324
|
+
when Array
|
|
325
|
+
obj = { lat: coord[0].to_f, lng: coord[1].to_f }
|
|
326
|
+
obj[:id] = coord[2].to_s if coord[2]
|
|
327
|
+
obj
|
|
328
|
+
when Hash
|
|
329
|
+
obj = { lat: coord[:lat].to_f, lng: coord[:lng].to_f }
|
|
330
|
+
obj[:id] = coord[:id].to_s if coord[:id]
|
|
331
|
+
obj
|
|
332
|
+
else
|
|
333
|
+
raise ArgumentError, "Invalid coordinate format: #{coord.inspect}"
|
|
334
|
+
end
|
|
335
|
+
end
|
|
336
|
+
|
|
337
|
+
# Build distance query parameters for GET requests
|
|
338
|
+
def build_distance_params(options)
|
|
339
|
+
params = {}
|
|
340
|
+
|
|
341
|
+
# Mode (map haversine to straightline)
|
|
342
|
+
if options[:mode]
|
|
343
|
+
mode = options[:mode].to_s
|
|
344
|
+
mode = 'straightline' if mode == 'haversine'
|
|
345
|
+
params[:mode] = mode
|
|
346
|
+
end
|
|
347
|
+
|
|
348
|
+
# Units (map kilometers to km for API compatibility)
|
|
349
|
+
if options[:units]
|
|
350
|
+
units = options[:units].to_s
|
|
351
|
+
units = 'km' if units == 'kilometers'
|
|
352
|
+
params[:units] = units
|
|
353
|
+
end
|
|
354
|
+
|
|
355
|
+
params[:max_results] = options[:max_results] if options[:max_results]
|
|
356
|
+
params[:max_distance] = options[:max_distance] if options[:max_distance]
|
|
357
|
+
params[:max_duration] = options[:max_duration] if options[:max_duration]
|
|
358
|
+
params[:min_distance] = options[:min_distance] if options[:min_distance]
|
|
359
|
+
params[:min_duration] = options[:min_duration] if options[:min_duration]
|
|
360
|
+
params[:order_by] = options[:order_by].to_s if options[:order_by]
|
|
361
|
+
params[:sort] = options[:sort].to_s if options[:sort]
|
|
362
|
+
|
|
363
|
+
params
|
|
364
|
+
end
|
|
365
|
+
|
|
366
|
+
# Build distance body parameters for POST requests
|
|
367
|
+
def build_distance_body_params(options)
|
|
368
|
+
params = {}
|
|
369
|
+
|
|
370
|
+
# Mode (map haversine to straightline)
|
|
371
|
+
if options[:mode]
|
|
372
|
+
mode = options[:mode].to_s
|
|
373
|
+
mode = 'straightline' if mode == 'haversine'
|
|
374
|
+
params[:mode] = mode
|
|
375
|
+
end
|
|
376
|
+
|
|
377
|
+
# Units (map kilometers to km for API compatibility)
|
|
378
|
+
if options[:units]
|
|
379
|
+
units = options[:units].to_s
|
|
380
|
+
units = 'km' if units == 'kilometers'
|
|
381
|
+
params[:units] = units
|
|
382
|
+
end
|
|
383
|
+
|
|
384
|
+
params[:max_results] = options[:max_results] if options[:max_results]
|
|
385
|
+
params[:max_distance] = options[:max_distance] if options[:max_distance]
|
|
386
|
+
params[:max_duration] = options[:max_duration] if options[:max_duration]
|
|
387
|
+
params[:min_distance] = options[:min_distance] if options[:min_distance]
|
|
388
|
+
params[:min_duration] = options[:min_duration] if options[:min_duration]
|
|
389
|
+
params[:order_by] = options[:order_by].to_s if options[:order_by]
|
|
390
|
+
params[:sort] = options[:sort].to_s if options[:sort]
|
|
391
|
+
|
|
392
|
+
params
|
|
393
|
+
end
|
|
394
|
+
|
|
395
|
+
# Build distance query string parts for geocode/reverse requests
|
|
396
|
+
# Returns an array of query string parts to be joined with '&'
|
|
397
|
+
def build_geocode_distance_query_parts(destinations, distance_mode, distance_units, distance_options)
|
|
398
|
+
parts = []
|
|
399
|
+
|
|
400
|
+
return parts unless destinations && !destinations.empty?
|
|
401
|
+
|
|
402
|
+
# Add destinations as properly formatted array parameters
|
|
403
|
+
destinations.each do |dest|
|
|
404
|
+
parts << "destinations[]=#{URI.encode_www_form_component(format_coordinate_string(dest))}"
|
|
405
|
+
end
|
|
406
|
+
|
|
407
|
+
# Mode (map haversine to straightline)
|
|
408
|
+
if distance_mode
|
|
409
|
+
mode = distance_mode.to_s
|
|
410
|
+
mode = 'straightline' if mode == 'haversine'
|
|
411
|
+
parts << "distance_mode=#{mode}"
|
|
412
|
+
end
|
|
413
|
+
|
|
414
|
+
# Units (map kilometers to km)
|
|
415
|
+
if distance_units
|
|
416
|
+
units = distance_units.to_s
|
|
417
|
+
units = 'km' if units == 'kilometers'
|
|
418
|
+
parts << "distance_units=#{units}"
|
|
419
|
+
end
|
|
420
|
+
|
|
421
|
+
# Additional options
|
|
422
|
+
if distance_options
|
|
423
|
+
parts << "distance_max_results=#{distance_options[:max_results]}" if distance_options[:max_results]
|
|
424
|
+
parts << "distance_max_distance=#{distance_options[:max_distance]}" if distance_options[:max_distance]
|
|
425
|
+
parts << "distance_max_duration=#{distance_options[:max_duration]}" if distance_options[:max_duration]
|
|
426
|
+
parts << "distance_min_distance=#{distance_options[:min_distance]}" if distance_options[:min_distance]
|
|
427
|
+
parts << "distance_min_duration=#{distance_options[:min_duration]}" if distance_options[:min_duration]
|
|
428
|
+
parts << "distance_order_by=#{distance_options[:order_by]}" if distance_options[:order_by]
|
|
429
|
+
parts << "distance_sort=#{distance_options[:sort]}" if distance_options[:sort]
|
|
430
|
+
end
|
|
431
|
+
|
|
432
|
+
parts
|
|
433
|
+
end
|
|
99
434
|
end
|
|
100
435
|
end
|
|
101
436
|
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: geocodio-gem
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.5.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- GoldenPavilion
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: exe
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date:
|
|
11
|
+
date: 2026-01-06 00:00:00.000000000 Z
|
|
12
12
|
dependencies: []
|
|
13
13
|
description: A Ruby Gem to help you integrate your application with the Geocodio API.
|
|
14
14
|
email:
|