mapricot-api 0.4.0 → 0.6.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.
Files changed (4) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +20 -12
  3. data/lib/mapricot/api.rb +84 -43
  4. metadata +1 -1
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ae3337cb4d6ca5a0dccab35644cf1ca98dde37f7bd74c0cd774a5be50f0e45c4
4
- data.tar.gz: 1bc1b3b515d89b390f99fc3e1c2cbca1572503fa040b4957c95158e61a6e4834
3
+ metadata.gz: ca2609b68cb960b86372cdb41ecb1ad1f0880e0eabdd7995b605e49f64cdb8e3
4
+ data.tar.gz: 9037e3ac81b3e91f9389bc586f3dc1430a076cc6385347f3bb04615830745898
5
5
  SHA512:
6
- metadata.gz: ec3514b04cdfbd59b403c83ddd2004d1ab364e41e0f1aafd7f43326def0cb5cb8352f78486ecf972ab62f4c4deeddaf14f4f411faf412af4da7302010c3a8e9d
7
- data.tar.gz: f09e7c6d84856e8bb8fb5210488c98fa300750d488ace6576204988acd33f3d427b09ca25c6a2cc1309ec0dd6e5f0a2fd65d7c15fe9c8ebc103bca7dbf077f67
6
+ metadata.gz: c6845a3ae2301ab0368d2d943b891942127064b8b99565fbd4f16dfba3f2e2b5e95fc67e13312b538813107d0bf680035a66d061dc99c44066f220cc35de9b4e
7
+ data.tar.gz: 9c6dc48367361ab545b4c74c684da88d9886207f9e17ec5a61e1c9115dc801cf1129389f3f9d7425d4f51a3e72f97b47ff936c57a5006ef4d47fa34ffd4ef9fe
data/README.md CHANGED
@@ -17,36 +17,44 @@ require "mapricot/api"
17
17
 
18
18
  client = Mapricot::Client.new(api_key: ENV.fetch("MAPRICOT_KEY"))
19
19
 
20
- data = client.search("128 SW 3rd Ave, Portland", limit: 1)
20
+ data = client.geocoding.search("128 SW 3rd Ave, Portland", limit: 1)
21
21
  place = data["results"][0]
22
22
  puts place["label"], place["lat"], place["lon"]
23
23
  ```
24
24
 
25
25
  ## Methods
26
26
 
27
- Each method returns the API's JSON as a Hash with string keys, with the same field names as the [API reference](https://mapricot.com/api-reference/).
27
+ Methods are grouped by product, the same way as the API's paths. Each returns the API's JSON as a Hash with string keys, with the same field names as the [API reference](https://mapricot.com/api-reference/).
28
28
 
29
29
  | Method | Endpoint |
30
30
  | --- | --- |
31
- | `client.search(q, focus:, country:, limit:, lang:)` | [`GET /geocoding/v1/search`](https://mapricot.com/docs/geocoding/#find-an-address) |
32
- | `client.autocomplete(q, focus:, country:, limit:, lang:)` | [`GET /geocoding/v1/autocomplete`](https://mapricot.com/docs/geocoding/#suggest-as-people-type) |
33
- | `client.reverse(lat, lon, limit:, lang:)` | [`GET /geocoding/v1/reverse`](https://mapricot.com/docs/geocoding/#find-the-address-at-a-point) |
34
- | `client.nearby(lat, lon, category:, radius:, limit:, lang:)` | [`GET /places/v1/nearby`](https://mapricot.com/docs/places/#list-places-around-a-point) |
35
- | `client.place(id, lang:)` | [`GET /places/v1/{id}`](https://mapricot.com/docs/places/#look-up-a-saved-place) |
36
- | `client.route(waypoints, mode:, geometries:)` | [`POST /routing/v1/route`](https://mapricot.com/docs/routing/#get-a-route) |
37
- | `client.ip(address)` | [`GET /ip-geolocation/v1/{ip}`](https://mapricot.com/docs/ip-geolocation/#look-up-an-address) |
31
+ | `client.geocoding.search(q, focus:, country:, limit:, lang:)` | [`GET /geocoding/v1/search`](https://mapricot.com/docs/geocoding/#find-an-address) |
32
+ | `client.geocoding.autocomplete(q, focus:, country:, limit:, lang:)` | [`GET /geocoding/v1/autocomplete`](https://mapricot.com/docs/geocoding/#suggest-as-people-type) |
33
+ | `client.geocoding.reverse(lat, lon, limit:, lang:)` | [`GET /geocoding/v1/reverse`](https://mapricot.com/docs/geocoding/#find-the-address-at-a-point) |
34
+ | `client.places.nearby(lat, lon, category:, radius:, limit:, lang:)` | [`GET /places/v1/nearby`](https://mapricot.com/docs/places/#list-places-around-a-point) |
35
+ | `client.places.get(id, lang:)` | [`GET /places/v1/{id}`](https://mapricot.com/docs/places/#look-up-a-saved-place) |
36
+ | `client.routing.route(waypoints, mode:, geometries:)` | [`POST /routing/v1/route`](https://mapricot.com/docs/routing/#get-a-route) |
37
+ | `client.routing.isochrone(location, minutes, mode:)` | [`POST /routing/v1/isochrone`](https://mapricot.com/docs/routing/#get-an-isochrone) |
38
+ | `client.ip_geolocation.lookup(ip)` | [`GET /ip-geolocation/v1/{ip}`](https://mapricot.com/docs/ip-geolocation/#look-up-an-address) |
38
39
  | `client.account` | `GET /account`, which checks a key and is never billed |
39
40
 
40
41
  Every keyword argument is optional. `focus` and each waypoint are `{ lat:, lon: }`. `country` and `category` take an Array or a comma-separated String.
41
42
 
42
43
  ```ruby
43
- nearby = client.nearby(45.52, -122.68, category: ["food:coffee_shop"], radius: 1000)
44
- route = client.route([{ lat: 45.5189, lon: -122.6793 }, { lat: 45.5214, lon: -122.6828 }], mode: "walk")
44
+ nearby = client.places.nearby(45.52, -122.68, category: ["food:coffee_shop"], radius: 1000)
45
+ route = client.routing.route([{ lat: 45.5189, lon: -122.6793 }, { lat: 45.5214, lon: -122.6828 }], mode: "walk")
45
46
  puts route["routes"][0]["durationSeconds"]
46
47
  ```
47
48
 
48
49
  A route's path comes as an encoded `polyline` with five decimal places, which polyline decoders assume. Pass `geometries: "geojson"` to get it as a GeoJSON `LineString` in `geometry` instead, which maps draw without decoding, or `geometries: "polyline6"` for six decimal places.
49
50
 
51
+ An isochrone is a GeoJSON `FeatureCollection` with one polygon per time, largest first, so a map that draws them in order shows the smaller ones on top:
52
+
53
+ ```ruby
54
+ area = client.routing.isochrone({ lat: 45.5189, lon: -122.6793 }, [5, 10], mode: "walk")
55
+ puts area["features"].map { |feature| feature["properties"]["minutes"] }.inspect
56
+ ```
57
+
50
58
  If your app shows results, show the credits in each response's `attribution`.
51
59
 
52
60
  ## Errors and retries
@@ -55,7 +63,7 @@ An error response raises `Mapricot::Error`, with the HTTP `status`, the API's er
55
63
 
56
64
  ```ruby
57
65
  begin
58
- client.search("")
66
+ client.geocoding.search("")
59
67
  rescue Mapricot::Error => e
60
68
  puts e.status, e.code, e.message # 400, invalid_request, The service rejected the request.
61
69
  end
data/lib/mapricot/api.rb CHANGED
@@ -1,12 +1,13 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "erb"
3
4
  require "json"
4
5
  require "net/http"
5
6
 
6
7
  # The official client for the Mapricot API: address and place search, reverse geocoding, nearby places, routing,
7
8
  # and IP geolocation. See https://mapricot.com/docs/ruby/.
8
9
  module Mapricot
9
- VERSION = "0.4.0"
10
+ VERSION = "0.6.0"
10
11
 
11
12
  # An error response from the API.
12
13
  class Error < StandardError
@@ -20,8 +21,11 @@ module Mapricot
20
21
  end
21
22
  end
22
23
 
23
- # Each method returns the API's JSON as a Hash with string keys, as in https://mapricot.com/api-reference/.
24
+ # Methods are grouped by product, as in client.geocoding.search. Each returns the API's JSON as a Hash with string
25
+ # keys, as in https://mapricot.com/api-reference/.
24
26
  class Client
27
+ attr_reader :geocoding, :places, :routing, :ip_geolocation
28
+
25
29
  def initialize(api_key:, base_url: "https://api.mapricot.com", timeout: 30, max_retries: 2)
26
30
  raise ArgumentError, "Pass api_key:, a Mapricot key." if api_key.nil? || api_key.empty?
27
31
 
@@ -29,43 +33,10 @@ module Mapricot
29
33
  @base_url = base_url
30
34
  @timeout = timeout
31
35
  @max_retries = max_retries
32
- end
33
-
34
- # Find addresses and places matching free text. focus is {lat:, lon:}; country is an Array or a String.
35
- def search(q, focus: nil, country: nil, limit: nil, lang: nil)
36
- get("/geocoding/v1/search", q: q, focus: focus, country: country, limit: limit, lang: lang)
37
- end
38
-
39
- # Suggest addresses and places as someone types.
40
- def autocomplete(q, focus: nil, country: nil, limit: nil, lang: nil)
41
- get("/geocoding/v1/autocomplete", q: q, focus: focus, country: country, limit: limit, lang: lang)
42
- end
43
-
44
- # Find the address nearest a coordinate, or the area around it.
45
- def reverse(lat, lon, limit: nil, lang: nil)
46
- get("/geocoding/v1/reverse", lat: lat, lon: lon, limit: limit, lang: lang)
47
- end
48
-
49
- # List places around a coordinate, optionally by category, an Array or a String.
50
- def nearby(lat, lon, category: nil, radius: nil, limit: nil, lang: nil)
51
- get("/places/v1/nearby", lat: lat, lon: lon, category: category, radius: radius, limit: limit, lang: lang)
52
- end
53
-
54
- # Look up one place by its Mapricot ID.
55
- def place(id, lang: nil)
56
- get("/places/v1/#{escape(id)}", lang: lang)
57
- end
58
-
59
- # Find a route through 2 to 20 waypoints, each {lat:, lon:}. mode is "drive" (the default), "walk", or "bicycle".
60
- # geometries is "polyline" (the default), "polyline6", or "geojson".
61
- def route(waypoints, mode: nil, geometries: nil)
62
- body = { waypoints: waypoints, mode: mode, geometries: geometries }.compact
63
- request(Net::HTTP::Post, URI("#{@base_url}/routing/v1/route"), JSON.generate(body))
64
- end
65
-
66
- # Approximate location of a public IP address.
67
- def ip(address)
68
- get("/ip-geolocation/v1/#{escape(address)}")
36
+ @geocoding = GeocodingService.new(method(:get))
37
+ @places = PlacesService.new(method(:get))
38
+ @routing = RoutingService.new(method(:post))
39
+ @ip_geolocation = IpGeolocationService.new(method(:get))
69
40
  end
70
41
 
71
42
  # The key's account. Not billed, so it checks a key for free.
@@ -82,6 +53,10 @@ module Mapricot
82
53
  request(Net::HTTP::Get, uri)
83
54
  end
84
55
 
56
+ def post(path, body)
57
+ request(Net::HTTP::Post, URI(@base_url + path), JSON.generate(body.compact))
58
+ end
59
+
85
60
  def request(method, uri, body = nil)
86
61
  attempt = 0
87
62
  loop do
@@ -114,10 +89,6 @@ module Mapricot
114
89
  end
115
90
  end
116
91
 
117
- def escape(segment)
118
- URI.encode_www_form_component(segment).gsub("+", "%20")
119
- end
120
-
121
92
  def error_from(response)
122
93
  request_id = response["X-Request-Id"]
123
94
  error = JSON.parse(response.body).fetch("error")
@@ -130,4 +101,74 @@ module Mapricot
130
101
  error.status == 503 || error.code == "concurrency_exceeded"
131
102
  end
132
103
  end
104
+
105
+ # client.geocoding: the /geocoding/v1 endpoints.
106
+ class GeocodingService
107
+ def initialize(get)
108
+ @get = get
109
+ end
110
+
111
+ # Find addresses and places matching free text. focus is {lat:, lon:}; country is an Array or a String.
112
+ def search(q, focus: nil, country: nil, limit: nil, lang: nil)
113
+ @get.call("/geocoding/v1/search", q: q, focus: focus, country: country, limit: limit, lang: lang)
114
+ end
115
+
116
+ # Suggest addresses and places as someone types.
117
+ def autocomplete(q, focus: nil, country: nil, limit: nil, lang: nil)
118
+ @get.call("/geocoding/v1/autocomplete", q: q, focus: focus, country: country, limit: limit, lang: lang)
119
+ end
120
+
121
+ # Find the address nearest a coordinate, or the area around it.
122
+ def reverse(lat, lon, limit: nil, lang: nil)
123
+ @get.call("/geocoding/v1/reverse", lat: lat, lon: lon, limit: limit, lang: lang)
124
+ end
125
+ end
126
+
127
+ # client.places: the /places/v1 endpoints.
128
+ class PlacesService
129
+ def initialize(get)
130
+ @get = get
131
+ end
132
+
133
+ # List places around a coordinate, optionally by category, an Array or a String.
134
+ def nearby(lat, lon, category: nil, radius: nil, limit: nil, lang: nil)
135
+ @get.call("/places/v1/nearby", lat: lat, lon: lon, category: category, radius: radius, limit: limit, lang: lang)
136
+ end
137
+
138
+ # Look up one place by its Mapricot ID.
139
+ def get(id, lang: nil)
140
+ @get.call("/places/v1/#{ERB::Util.url_encode(id)}", lang: lang)
141
+ end
142
+ end
143
+
144
+ # client.routing: the /routing/v1 endpoints.
145
+ class RoutingService
146
+ def initialize(post)
147
+ @post = post
148
+ end
149
+
150
+ # Find a route through 2 to 20 waypoints, each {lat:, lon:}. mode is "drive" (the default), "walk", or "bicycle".
151
+ # geometries is "polyline" (the default), "polyline6", or "geojson".
152
+ def route(waypoints, mode: nil, geometries: nil)
153
+ @post.call("/routing/v1/route", { waypoints: waypoints, mode: mode, geometries: geometries })
154
+ end
155
+
156
+ # The areas reachable from a location, {lat:, lon:}, within 1 to 4 times, each 1 to 60 minutes, as a GeoJSON
157
+ # FeatureCollection with one polygon per time, largest first. mode is as for route.
158
+ def isochrone(location, minutes, mode: nil)
159
+ @post.call("/routing/v1/isochrone", { location: location, minutes: minutes, mode: mode })
160
+ end
161
+ end
162
+
163
+ # client.ip_geolocation: the /ip-geolocation/v1 endpoints.
164
+ class IpGeolocationService
165
+ def initialize(get)
166
+ @get = get
167
+ end
168
+
169
+ # Approximate location of a public IP address.
170
+ def lookup(ip)
171
+ @get.call("/ip-geolocation/v1/#{ERB::Util.url_encode(ip)}")
172
+ end
173
+ end
133
174
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: mapricot-api
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.0
4
+ version: 0.6.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Mapricot