├── .gitignore
├── .travis.yml
├── LICENSE
├── README.md
├── bash
├── README.md
└── mapbox_geocode.sh
├── batch_node
├── README.md
├── mapbox-batch.js
└── package.json
├── batch_python
├── README.md
├── mapbox_batch.py
└── requirements.txt
├── node
├── README.md
└── mapbox-geocode.js
├── php
├── GeocodeResponse.php
├── Mapbox.php
├── MapboxApiException.php
├── MapboxQuery.php
├── MapboxResponse.php
├── MapboxTest.php
├── README.md
└── test.php
├── python
├── README.md
└── mapbox_geocode.py
└── test
├── sample.txt
└── test.sh
/.gitignore:
--------------------------------------------------------------------------------
1 | .virtualenv
2 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: node_js
2 | cache: apt
3 | node_js:
4 | - 0.10.28
5 | before_install:
6 | - sudo apt-get update -qq
7 | - sudo apt-get install -qq -y python-setuptools python-pip python-virtualenv python3-dev
8 | script:
9 | - "./test/test.sh"
10 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (c) 2015, Mapbox <>
2 |
3 | Permission to use, copy, modify, and/or distribute this software for any
4 | purpose with or without fee is hereby granted, provided that the above
5 | copyright notice and this permission notice appear in all copies.
6 |
7 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
8 | WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
9 | MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
10 | ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
11 | WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
12 | ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
13 | OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
14 |
15 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # geocoding-example
2 | [](https://travis-ci.org/mapbox/geocoding-example)
3 |
4 | Examples of how to use the Mapbox geocoder. We do our best to make our [geocoding API simple to understand](https://docs.mapbox.com/api/search/#geocoding). But sometimes it's nice to see example implementations of things like batch geocoding where parallelization and throttling are necessary.
5 |
6 | This repo is a work in progress. Don't see your favorite language? [Open a ticket](https://github.com/mapbox/geocoding-example/issues/new)!
7 |
8 | Please consult individual subdirectories' README files for more specific installation instructions and notes. PHP Examples are available in the [Mapbox PHP SDK Repo](https://github.com/mapbox/mapbox-sdk-php).
9 |
10 | ## Note
11 |
12 | Permanent and batch geocoding is currently only available for enterprise plans. Please email sales@mapbox.com if you're interested in testing this functionality.
13 |
--------------------------------------------------------------------------------
/bash/README.md:
--------------------------------------------------------------------------------
1 | # bash
2 |
3 | `mapbox_geocode.sh` takes a single query as a parameter and outputs the result.
4 |
5 | ## Requirements
6 |
7 | - the `hexdump` utility (standard on OS X, Ubuntu, Debian & most other distributions)
8 | - A Mapbox access token with geocoding capabilities
9 |
10 | ## Usage
11 |
12 | ```
13 | MapboxAccessToken=YOUR_ACCESS_TOKEN ./mapbox_geocode.sh '1600 Pennsylvania Ave Washington, DC'
14 | ```
--------------------------------------------------------------------------------
/bash/mapbox_geocode.sh:
--------------------------------------------------------------------------------
1 | # check for access token
2 | if [ -z "$MapboxAccessToken" ]; then
3 | echo "You must specify a valid MapboxAccessToken environment variable"
4 | exit 1
5 | fi
6 |
7 | # escape query
8 | QUERY="$(echo -ne "$1" | hexdump -v -e '/1 "%02x"' | sed 's/\(..\)/%\1/g')"
9 |
10 | # send query
11 | curl -s "https://api.tiles.mapbox.com/geocoding/v5/mapbox.places-permanent/${QUERY}.json?access_token=${MapboxAccessToken}"
--------------------------------------------------------------------------------
/batch_node/README.md:
--------------------------------------------------------------------------------
1 | # Node.js - Mapbox Batch Geocoder
2 |
3 | `mapbox_batch.js` reads queries from an input text file, one per line, and constructs parallelized batch queries.
4 |
5 | Results are stored in an output directory as JSON files.
6 |
7 | ## Requirements
8 |
9 | - [queue-async](https://www.npmjs.com/package/queue-async)
10 | - [through](https://www.npmjs.com/package/through)
11 | - A Mapbox access token with batch geocoding capabilities (email sales@mapbox.com)
12 |
13 | ## Installation
14 |
15 | `npm install`
16 |
17 | ## Usage
18 |
19 | Command line:
20 | ```
21 | MapboxAccessToken=YOUR_ACCESS_TOKEN node mapbox-batch.js input_file.txt /path/to/output/directory
22 | ```
23 |
24 | Programmatic:
25 | ```
26 | var fs = require('fs'),
27 | mapbox = require('./mapbox-batch.js');
28 |
29 | var geocoder = mapbox('YOUR_ACCESS_TOKEN');
30 | geocoder.on('data', function(data) {
31 | console.log(data);
32 | });
33 | fs.createReadStream('/path/to/source/data.txt').pipe(geocoder);
34 | ```
35 |
--------------------------------------------------------------------------------
/batch_node/mapbox-batch.js:
--------------------------------------------------------------------------------
1 | var fs = require('fs'),
2 | https = require('https'),
3 | path = require('path'),
4 | through = require('through'),
5 | queue = require('queue-async');
6 |
7 | function MapboxBatchGeocoder(mapboxAccessToken, batchSize, parallelism) {
8 | batchSize = (batchSize !== undefined) ? batchSize : 50;
9 | parallelism = (parallelism !== undefined) ? parallelism: 5;
10 |
11 | var queries = [],
12 | queryBuffer = '',
13 | q = queue(parallelism);
14 |
15 | function geocode(queries, callback) {
16 | q.defer(function(cb) {
17 | var sent = +new Date();
18 | https.get('https://api.tiles.mapbox.com/geocoding/v5/mapbox.places-permanent/' + queries.map(encodeURIComponent).join(';') + '.json?access_token=' + mapboxAccessToken,
19 | function(response) {
20 | var body = '';
21 | response.on('data', function(d) {
22 | body += d;
23 | });
24 | response.on('error', function(e) {
25 | callback(e);
26 | });
27 | response.on('end', function() {
28 | callback(null, body);
29 | setTimeout(cb, ((1000 * batchSize * parallelism * parseFloat(response.headers['x-rate-limit-interval'])) / parseFloat(response.headers['x-rate-limit-limit'])) - (+new Date() - sent) );
30 | });
31 | });
32 | });
33 | }
34 |
35 | function emitResult(err, data) {
36 | if (err) return console.log('Error: ' + err);
37 | this.emit('data', data);
38 | }
39 |
40 | function thruOut(data) {
41 | var that = this;
42 | queryBuffer += data;
43 | var potentialQueries = queryBuffer.split('\n');
44 | potentialQueries.forEach(function(part, part_i) {
45 | if (part_i === (potentialQueries.length-1))
46 | queryBuffer = part;
47 | else
48 | queries.push(part);
49 |
50 | if (queries.length >= batchSize) {
51 | geocode(queries, emitResult.bind(that));
52 | queries = [];
53 | }
54 | });
55 | }
56 |
57 | function thruEnd() {
58 | if (queries.length > 0) geocode(queries, emitResult.bind(this));
59 | }
60 |
61 | return through(thruOut, thruEnd);
62 | }
63 |
64 | if (require.main === module) {
65 | if (!process.env.MapboxAccessToken) {
66 | console.log('environment variable MapboxAccessToken must be set');
67 | process.exit(1);
68 | }
69 |
70 | var responseIndex = 0;
71 | var mapbox = MapboxBatchGeocoder(process.env.MapboxAccessToken, 50, 5);
72 | mapbox.on('data', function(data) {
73 | fs.writeFile(path.resolve(process.argv[3] + '/' + responseIndex + '.json'), data);
74 | console.log('storing ' + path.normalize(process.argv[3] + '/' + responseIndex) + '.json');
75 | responseIndex++;
76 | });
77 | fs.createReadStream(process.argv[2]).pipe(mapbox);
78 | }
79 |
80 | module.exports = MapboxBatchGeocoder;
--------------------------------------------------------------------------------
/batch_node/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "mapbox-batch-geocoder",
3 | "version": "1.0.0",
4 | "description": "Sample code to use Mapbox's permanent geocoding endpoint in batch mode",
5 | "main": "mapbox-batch.js",
6 | "scripts": {
7 | "test": "echo \"Error: no test specified\" && exit 1"
8 | },
9 | "author": "",
10 | "license": "ISC",
11 | "dependencies": {
12 | "queue-async": "^1.0.7",
13 | "through": "^2.3.8"
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/batch_python/README.md:
--------------------------------------------------------------------------------
1 | # Python 2/3 - Mapbox Batch Geocoder
2 |
3 | `mapbox_batch.py` reads queries from an input text file, one per line, and constructs parallelized batch queries.
4 |
5 | Results are stored in an output directory as JSON files.
6 |
7 | ## Requirements
8 |
9 | - [gevent 1.1.2 or later](https://pypi.python.org/pypi/gevent)
10 | - [requests](http://docs.python-requests.org/en/latest/)
11 | - A Mapbox access token with batch geocoding capabilities (email sales@mapbox.com)
12 |
13 | ## Installation
14 |
15 | `pip install -r requirements.txt`
16 |
17 | ## Usage
18 |
19 | Command line:
20 | ```
21 | MapboxAccessToken=YOUR_ACCESS_TOKEN python mapbox_batch.py input_file.txt /path/to/output/directory
22 | ```
23 |
24 | Programmatic:
25 | ```
26 | from mapbox_batch import MapboxBatchGeocoder
27 |
28 | mapbox = MapboxBatchGeocoder('YOUR_ACCESS_TOKEN')
29 | with open('/path/to/input_file.txt') as input_file:
30 | mapbox.geocode(input_file, '/path/to/output/directory/')
31 | ```
32 |
--------------------------------------------------------------------------------
/batch_python/mapbox_batch.py:
--------------------------------------------------------------------------------
1 | import __future__
2 | import json, sys, urllib, os.path, time
3 | import requests, gevent, gevent.pool
4 | try:
5 | from urllib.parse import quote_plus as quote_plus
6 | except:
7 | from urllib import quote_plus as quote_plus
8 |
9 | class MapboxBatchGeocoder(object):
10 | """
11 | Sample implementation of batch geocoding with rate limiting & concurrency.
12 |
13 | Args:
14 | mapbox_access_token (str): valid Mapbox access token with permanent geocoding permissions
15 | batch_size (Optional[int]): number of features to geocode per query
16 | parallelism (Optional[int]): number of simultaneous http connections to use. None = no limit.
17 | """
18 | def __init__(self, mapbox_access_token, batch_size=50, parallelism=5):
19 | self.mapbox_access_token = mapbox_access_token
20 | self.batch_size = batch_size
21 | self.ratelimit_delay = None # initial value; ignored after first response
22 | if parallelism is None: # None = use as many http connections as ratelimit allows
23 | self.spawner = gevent
24 | else:
25 | self.spawner = gevent.pool.Pool(parallelism)
26 |
27 | def _chunks(self, f, chunk_size):
28 | chunk = []
29 | for line in f:
30 | chunk.append(line)
31 | if len(chunk) >= chunk_size:
32 | yield chunk
33 | chunk = []
34 | if len(chunk):
35 | yield chunk
36 |
37 | def _send(self, chunk, path):
38 | response = requests.get('https://api.tiles.mapbox.com/geocoding/v5/mapbox.places-permanent/{}.json?access_token={}'.format(';'.join([quote_plus(s.strip()) for s in chunk]), self.mapbox_access_token))
39 | if response.status_code == 200:
40 | print('- response received, saving to {}'.format(path))
41 | with open(path, 'w') as output_file:
42 | json.dump(response.json(), output_file, indent=4)
43 | self.ratelimit_delay = ((self.batch_size * float(response.headers.get('x-rate-limit-interval'))) / float(response.headers.get('x-rate-limit-limit'))) + 0.1
44 | else:
45 | print('# {} error: {}'.format(response.status_code, response.text))
46 |
47 | def geocode(self, src, dst):
48 | """
49 | Convert queries to output file(s).
50 |
51 | Args:
52 | src (file-like): a file-like object of queries (one per line)
53 | dst (str): the system path into which results will be placed (as Carmen GeoJSON files)
54 | """
55 | for (i, chunk) in enumerate(self._chunks(src, self.batch_size)):
56 | output_filename = '{}/{}.json'.format(dst, i)
57 | if i == 0:
58 | self._send(chunk, output_filename) # block on first call to get ratelimit
59 | else:
60 | self.spawner.spawn(self._send, chunk, output_filename)
61 | gevent.sleep(self.ratelimit_delay)
62 |
63 | if __name__ == '__main__':
64 | MAPBOX_ACCESS_TOKEN = os.environ.get('MapboxAccessToken', False)
65 | if not MAPBOX_ACCESS_TOKEN:
66 | print('environment variable MapboxAccessToken must be set')
67 | sys.exit(1)
68 |
69 | (input_path, output_path) = map(os.path.abspath, sys.argv[1:3])
70 | for path in (input_path, output_path):
71 | if not os.path.exists(path):
72 | print('{} does not exist'.format(path))
73 | sys.exit(1)
74 |
75 | mapbox = MapboxBatchGeocoder(MAPBOX_ACCESS_TOKEN)
76 | with open(input_path, 'r') as input_file:
77 | mapbox.geocode(input_file, output_path)
78 |
--------------------------------------------------------------------------------
/batch_python/requirements.txt:
--------------------------------------------------------------------------------
1 | gevent==1.1.2
2 | greenlet==0.4.7
3 | requests==2.7.0
4 |
--------------------------------------------------------------------------------
/node/README.md:
--------------------------------------------------------------------------------
1 | # Node.js - Mapbox Geocoder
2 |
3 | `mapbox-geocode.js` takes a single query as a parameter and outputs the result.
4 |
5 | ## Requirements
6 |
7 | - Node.js
8 | - A Mapbox access token with geocoding capabilities
9 |
10 | ## Usage
11 |
12 | Command line:
13 | ```
14 | MapboxAccessToken=YOUR_ACCESS_TOKEN node ./mapbox-geocode.js '1600 Pennsylvania Ave Washington, DC'
15 | ```
16 |
17 | Programmatic:
18 | ```
19 | > var mapbox = require('./mapbox-geocode.js');
20 | > mapbox('YOUR_ACCESS_TOKEN', '1600 Pennsylvania Ave Washington, DC', function(err, data) { console.log(data); });
21 |
22 | { type: 'FeatureCollection',
23 | query: [ '1600', 'pennsylvania', 'ave', 'washington', 'dc' ],
24 | features:
25 | [ { id: 'address.170282823806239',
26 | type: 'Feature',
27 | text: 'Pennsylvania Ave NW',
28 | place_name: '1600 Pennsylvania Ave NW, Washington, 20006, District of Columbia, United States',
29 | relevance: 0.863516129032258,
30 | center: [Object],
31 | geometry: [Object],
32 | bbox: [Object],
33 | address: '1600',
34 | properties: {},
35 | context: [Object] },
36 | [...]
37 | ```
--------------------------------------------------------------------------------
/node/mapbox-geocode.js:
--------------------------------------------------------------------------------
1 | var https = require('https');
2 |
3 | function geocode(mapboxAccessToken, query, callback) {
4 | https.get('https://api.tiles.mapbox.com/geocoding/v5/mapbox.places/' + encodeURIComponent(query) + '.json?access_token=' + mapboxAccessToken,
5 | function(response) {
6 | var body = '';
7 | response.on('data', function(d) {
8 | body += d;
9 | });
10 | response.on('error', function(e) {
11 | callback(e);
12 | });
13 | response.on('end', function() {
14 | callback(null, JSON.parse(body));
15 | });
16 | });
17 | }
18 |
19 | if (require.main === module) {
20 | if (!process.env.MapboxAccessToken) {
21 | console.log('environment variable MapboxAccessToken must be set');
22 | process.exit(1);
23 | }
24 | geocode(process.env.MapboxAccessToken, process.argv[2], function(err, result) {
25 | if (err) return console.log('Error: ' + err);
26 | console.log(JSON.stringify(result, null, 2));
27 | });
28 | }
29 |
30 | module.exports = geocode;
--------------------------------------------------------------------------------
/php/GeocodeResponse.php:
--------------------------------------------------------------------------------
1 | resultCount = count($this->body['features']);
24 | $this->attribution = $this->body['attribution'];
25 | $this->type = $this->body['type'];
26 | $this->query = $this->body['query'];
27 | $this->features = $this->body['features'];
28 |
29 | //assign data
30 | $this->assignData($this->body['features']);
31 |
32 | return true;
33 | }
34 |
35 | /**
36 | * Gets response
37 | */
38 | public function getData(){
39 | return $this->features;
40 | }
41 |
42 | /**
43 | * Gets count
44 | */
45 | public function getCount(){
46 | return $this->resultCount;
47 | }
48 |
49 | /**
50 | * Gets attribution
51 | */
52 | public function getAttribution(){
53 | return $this->attribution;
54 | }
55 |
56 | /**
57 | * Assigns data element to object for iterator
58 | * @param array data The data array from API response
59 | */
60 | protected function assignData($data){
61 | if ($data){
62 | //assign data to iterator
63 | foreach ($data as $index => $datum){
64 | $this[$index] = $datum;
65 | }
66 | }
67 | }
68 |
69 | }
70 | ?>
71 |
--------------------------------------------------------------------------------
/php/Mapbox.php:
--------------------------------------------------------------------------------
1 | 'v5'); //versions for endpoint
17 | protected $debug = false; //debug flag
18 | protected $token; //access token
19 | protected $curlTimeout = 0; //maximum number of seconds for the network function to execute (0 = no timeout)
20 | protected $connectTimeout = 0; //maximum number of seconds to connect to the server (0 = no timeout)
21 | protected $placeTypes = array('country','region','postcode','place','neighborhood','address','poi');
22 | protected $permanentGeocodes = false; //changes geocding endpoint when flipped
23 |
24 | /**
25 | * Constructor. Creates authenticated access to Mapbox.
26 | * @param string token your Mapbox token.
27 | */
28 | public function __construct($token) {
29 | //register autoloader
30 | spl_autoload_register(array (
31 | get_class(),
32 | 'MapboxAutoload'
33 | ));
34 | $this->token = $token;
35 | }
36 |
37 | /** Gets token **/
38 | public function getToken(){
39 | return $this->token;
40 | }
41 |
42 | /**
43 | * Sets version for endpoint
44 | * @param string endpoint endpoint name e.g. 'geocoder'
45 | * @param string endpoint version e.g. 'v4'
46 | **/
47 | public function setEndpointVersion($endpoint,$version) {
48 | $this->versions[$endpoint] = $version;
49 | }
50 |
51 | /**
52 | * Turns on debugging for output to stderr
53 | */
54 | public function debug() {
55 | $this->debug = true;
56 | }
57 |
58 | /**
59 | * Change the base URL at which to contact Mapbox's API. This
60 | * may be useful if you want to talk to a test or staging
61 | * server withou changing config
62 | * Example value: http://staging.api.v3.Mapbox.com/t/
63 | * @param urlBase the base URL at which to contact Mapbox's API.
64 | * @return void
65 | */
66 | public function setHome($urlBase) {
67 | $this->home = $urlBase;
68 | }
69 |
70 | protected function getGeocoderDataSet(){
71 | if ($this->permanentGeocodes == true){
72 | $dataSet = "mapbox.places-permanent";
73 | } else {
74 | $dataSet = "mapbox.places";
75 | }
76 | return $dataSet;
77 | }
78 |
79 | protected function urlForGeocode($query) {
80 | return $this->home ."/geocoding/".$this->versions['geocoder']."/".$this->getGeocoderDataSet()."/" . urlencode($query).".json";
81 | }
82 |
83 | protected function urlForReverseGeocode($longitude, $latitude) {
84 | return $this->home ."/geocoding/".$this->versions['geocoder']."/".$this->getGeocoderDataSet()."/" .$longitude.",".$latitude.".json";
85 | }
86 |
87 | /**
88 | * Geocodes by returning a response containing the address nearest a given point.
89 | * @param string query The unstructured address or place-name
90 | * @param array types containing n of country, region, postcode, place, neighborhood, address, or poi
91 | * @param array proximity with keys 'longitude' , 'latitude'
92 | * @return the response of a geocode query against Mapbox.
93 | */
94 | public function geocode($query, $types=array(), $proximity=array()) {
95 | $params = array();
96 | if (empty($query)){return null;}
97 | $url = $this->urlForGeocode($query);
98 | if (!empty($types)){
99 | $params['types'] = $types;
100 | }
101 | if (!empty($proximity)){
102 | $params['proximity'] = $proximity['longitude'].",".$proximity['latitude'];
103 | }
104 | $this->permanentGeocodes = false; //set by default to off
105 | return new GeocodeResponse($this->request($url,"GET",$params));
106 | }
107 |
108 | /** Different endpoint for permanent geocodes **/
109 | public function geocodePermanent($query, $types=array(), $proximity=array()) {
110 | $this->permanentGeocodes = true;
111 | return $this->geocode($query, $types=array(), $proximity=array());
112 | }
113 |
114 | /**
115 | * Reverse geocodes by returning a response containing the resolved entities.
116 | * @param obj point The point for which the nearest address is returned
117 | * @param string tableName Optional. The tablenae to geocode against. Currently only 'places' is supported.
118 | * @return the response of running a reverse geocode query for point against Mapbox.
119 | */
120 | public function reverseGeocode($longitude, $latitude, $types=array()) {
121 | $params = array();
122 | $url = $this->urlForReverseGeocode($longitude, $latitude);
123 | if (!empty($types)){
124 | $params['types'] = $types;
125 | }
126 | return new GeocodeResponse($this->request($url,"GET",$params));
127 | }
128 |
129 | /**
130 | * Sign the request, perform a curl request and return the results
131 | *
132 | * @param string $urlStr unsigned URL request
133 | * @param string $requestMethod
134 | * @param null $params
135 | * @return array ex: array ('headers'=>array(), 'body'=>string)
136 | * @throws MapboxApiException
137 | */
138 | protected function request($urlStr, $requestMethod="GET", $params = null) {
139 | //custom input headers
140 | $curlOptions[CURLOPT_HTTPHEADER] = array ();
141 | $curlOptions[CURLOPT_HTTPHEADER][] = "X-Mapbox-Lib: " . $this->driverVersion;
142 | if ($requestMethod == "POST") {
143 | $curlOptions[CURLOPT_HTTPHEADER][] = "Content-Type: " . "application/x-www-form-urlencoded";
144 | }
145 |
146 | //request curl returns server headers
147 | $curlOptions[CURLOPT_RETURNTRANSFER] = 1;
148 | $curlOptions[CURLOPT_HEADER] = 1;
149 |
150 | //other curl options
151 | $curlOptions[CURLOPT_CONNECTTIMEOUT] = $this->connectTimeout; //connection timeout
152 | $curlOptions[CURLOPT_TIMEOUT] = $this->curlTimeout; //execution timeout
153 | $curlOptions[CURLOPT_RETURNTRANSFER] = 1; //return contents on success
154 |
155 | //format query parameters and append
156 | $formattedParams = null;
157 | if (count($params)>0){
158 | foreach ($params as $key=>$value){
159 | if (is_array($value)){
160 | $keyVal[] = $key."=".implode(",",$value);
161 | } else {
162 | $keyVal[] = $key."=".$value;
163 | }
164 | }
165 | $formattedParams .= implode("&",$keyVal);
166 | }
167 |
168 | //url formatting
169 | if ($formattedParams){
170 | $urlStr .= "?".$formattedParams;
171 | $url = $urlStr."&access_token=".$this->token;
172 | } else {
173 | $url = $urlStr."?access_token=".$this->token;
174 | }
175 |
176 | //format cURL
177 | $ch = curl_init($url);
178 | foreach ($curlOptions as $key=>$value){
179 | curl_setopt ($ch , $key, $value);
180 | }
181 |
182 | //init metadata
183 | $info = array();
184 | $info['request']['encoded'] = $urlStr;
185 | $info['request']['unencoded'] = urldecode($urlStr);
186 | $info['driver'] = $this->driverVersion;
187 | $info['request']['method'] = $requestMethod;
188 |
189 | //make request
190 | try {
191 | $callStart = microtime(true);
192 | $result = curl_exec($ch);
193 | $callEnd = microtime(true);
194 | } catch (Exception $e) {
195 | //catch client exception
196 | $info['message'] = "Service exception. Client did not connect and returned '" . $e->getMessage() . "'";
197 | $MapboxE = new MapboxApiException($info);
198 | throw $MapboxE;
199 | }
200 |
201 | //add execution time
202 | $info['request']['time'] = $callEnd - $callStart;
203 |
204 | //extract Mapbox headers
205 | $headerSize = curl_getinfo($ch, CURLINFO_HEADER_SIZE);
206 | $header = substr($result, 0, $headerSize);
207 | $result = substr($result, $headerSize);
208 | $headers = explode(PHP_EOL, $header);
209 | $headers = array_filter($headers,"trim");
210 |
211 | //extract curl info
212 | $info['code'] = (int) curl_getinfo($ch, CURLINFO_HTTP_CODE);
213 | if ($params) {
214 | $info['request']['parameters'] = $params;
215 | }
216 |
217 | //catch server exception & load up on debug data
218 | if ($info['code'] >= 400 | $this->debug) {
219 | //get a boatload of debug data
220 | $info['headers'] = $headers;
221 | $info['curl'] = curl_getinfo($ch);
222 | //write debug info to stderr if debug mode on
223 | if ($this->debug) {
224 | $info = array_filter($info); //remove empty elements for readability
225 | file_put_contents('php://stderr', "Debug " . print_r($info, true));
226 | }
227 | //chuck exception with some helpful errors for the most common codes
228 | if ($info['code'] >= 400){
229 | switch ($info['code']) {
230 | case 401:
231 | $info['message'] = "401 Unauthorized; check your access token";
232 | break;
233 | case 403:
234 | $info['message'] = "403 Verboten; you do not have access to this resource -- some endpoints require authorization from Mapbox";
235 | break;
236 | case 429:
237 | $info['message'] = "429 Enhance your calm. Exceeding rate limits -- use this::debug to see server response headers";
238 | break;
239 | case 422:
240 | $info['message'] = "422 Unprocessable Entity; check your parameter values, esp swapped lat/lons, because we all do this";
241 | break;
242 | default:
243 | $info['message'] = "HTTP code ".$info['code'].": use this::debug to see server response headers";
244 | break;
245 | }
246 | $MapboxE = new MapboxApiException($info);
247 | throw $MapboxE;
248 | }
249 | }
250 |
251 | //close curl
252 | curl_close($ch);
253 |
254 | //format
255 | $res['info'] = $info;
256 | $res['headers'] = $headers;
257 | $res['body'] = $result;
258 |
259 | return $res;
260 | }
261 |
262 |
263 | /**
264 | * Autoloader for file dependencies
265 | * Called by spl_autoload_register() to avoid conflicts with autoload() methods from other libs
266 | */
267 | public static function mapboxAutoload($className) {
268 | $filename = dirname(__FILE__) . "/" . $className . ".php";
269 | // don't interfere with other classloaders
270 | if (!file_exists($filename)) {
271 | return;
272 | }
273 | include $filename;
274 | }
275 |
276 | /**
277 | * Sets maximum number of seconds to connect to the server before bailing
278 | * @param int secs timeout in seconds
279 | */
280 | public function setConnectTimeout($secs){
281 | $this->connectTimeout = $secs;
282 | return $this;
283 | }
284 |
285 | /**
286 | * Sets maximum number of seconds to the network function to execute
287 | * @param int secs timeout in seconds
288 | */
289 | public function setCurlTimeout($secs){
290 | $this->curlTimeout = $secs;
291 | return $this;
292 | }
293 | }
294 | ?>
295 |
--------------------------------------------------------------------------------
/php/MapboxApiException.php:
--------------------------------------------------------------------------------
1 | info = $info;
15 | if (isset($info['message'])){
16 | $this->message = $info['message'];
17 | $this->code = $info['code'];
18 | } else {
19 | $this->message = "Unknown error; no message returned from server";
20 | }
21 | }
22 |
23 | public function debug(){
24 | return $this->info;
25 | }
26 | }
27 | ?>
28 |
--------------------------------------------------------------------------------
/php/MapboxQuery.php:
--------------------------------------------------------------------------------
1 | limit = $limit;
36 | return $this;
37 | }
38 |
39 | /**
40 | * Sets how many records in to start getting results (i.e., the page offset) for this Query.
41 | * @param int offset The page offset for this Query.
42 | * @return obj this Query
43 | */
44 | public function offset($offset) {
45 | $this->offset = $offset;
46 | return $this;
47 | }
48 |
49 | /**
50 | * Builds and returns the query string to represent this Query when talking to
51 | * Mapbox's API. Provides proper URL encoding and escaping.
52 | * @return string The query string to represent this Query when talking to Mapbox's API.
53 | * @internal re-activate geobounds method
54 | */
55 | public function toUrlQuery() {
56 |
57 | $temp['select'] = $this->fieldsJsonOrNull();
58 | $temp['q'] = $this->fullTextSearch;
59 | $temp['sort'] = $this->sortsJsonOrNull();
60 | $temp['limit'] = ($this->limit > 0 ? $this->limit : null);
61 | $temp['offset'] = ($this->offset > 0 ? $this->offset : null);
62 | $temp['include_count'] = ($this->includeRowCount ? "true" : null);
63 | $temp['filters'] = $this->rowFiltersJsonOrNull();
64 | $temp['geo'] = $this->geoBoundsJsonOrNull();
65 | $temp['threshold'] = $this->thresholdOrNull();
66 | $temp = array_filter($temp); //remove nulls
67 |
68 | //initialize
69 | $temp2 = array();
70 |
71 | //encode (cannot use http_build_query() as we need to *raw* encode adn this not provided until PHP v5.4)
72 | foreach ($temp as $key => $value){
73 | $temp2[] = $key."=".rawurlencode($value);
74 | }
75 |
76 | //process additional kay/value parameters
77 | foreach ($this->keyValuePairs as $key => $value){
78 | $temp2[] = $key."=".rawurlencode($value);
79 | }
80 |
81 | return implode("&", $temp2);
82 | }
83 |
84 | /**
85 | * Adds misc parameters to the URL query
86 | * @param string key Key namev
87 | * @param string un-URL-encoded value
88 | */
89 | public function addParam($key,$value){
90 | $this->keyValuePairs[$key] = $value;
91 | return $this->keyValuePairs;
92 | }
93 |
94 | /**
95 | * Adds array of name/key pairs to query for eventual resolution
96 | * @param array keyValueArray A key value array
97 | * $return object This query object or NULL on failure
98 | */
99 | public function addParamArray($keyValueArray) {
100 | if (!is_array($keyValueArray)){
101 | throw new exception (__METHOD__." Parameter must be array: key = attribute name, value = attribute value");
102 | }
103 | foreach($keyValueArray as $key => $value) {
104 | $this->keyValuePairs[$key] = $value;
105 | }
106 | return $this;
107 | }
108 |
109 |
110 | public function toString() {
111 | try {
112 | return urldecode($this->toUrlQuery());
113 | } catch (Exception $e) {
114 | throw $e;
115 | }
116 | }
117 |
118 |
119 | }
120 | ?>
121 |
--------------------------------------------------------------------------------
/php/MapboxResponse.php:
--------------------------------------------------------------------------------
1 | json = $apiResponse['body']; //raw json
21 | $this->body = json_decode($apiResponse['body'],true);
22 | $this->info = $apiResponse['info'];
23 | $this->headers = $apiResponse['headers'];
24 | $this->parseResponse($apiResponse);
25 | } catch (Exception $e) {
26 | //add note about json encoding borking here
27 | throw $e;
28 | }
29 | }
30 |
31 | /**
32 | * Parses the entire response, incl metadata
33 | * @param array apiResponse response from curl
34 | * @return void
35 | */
36 | protected function parseResponse(){
37 | return true;
38 | }
39 |
40 | /**
41 | * Get HTTP response code
42 | * @return int
43 | */
44 | public function getResponseCode(){
45 | return $this->info['code'];
46 | }
47 |
48 | /**
49 | * Test for success (200 status return)
50 | * Note this tests for a successful http call, not a successful program operation
51 | */
52 | public function success(){
53 | if ($this->info['code'] == 200){
54 | return true;
55 | } else {
56 | return false;
57 | }
58 | }
59 |
60 | /**
61 | * Get the entire JSON response from Mapbox
62 | * @return string
63 | */
64 | public function getJson() {
65 | return $this->json;
66 | }
67 |
68 | /**
69 | * Gets count of elements returned in this page of result set (not total count)
70 | * @return int
71 | */
72 | public function size() {
73 | return count($this);
74 | }
75 |
76 | /**
77 | * Subclasses of MapboxResponse must provide access to the original JSON
78 | * representation of Mapbox's response. Alias for getJson()
79 | * @return string
80 | */
81 | public function toString() {
82 | return $this->getJson();
83 | }
84 |
85 | /**
86 | * Get url-decoded request string, does not include auth.
87 | * @return string
88 | */
89 | public function getRequest(){
90 | return $this->info['request']['unencoded'];
91 | }
92 |
93 | /**
94 | * Get url-encoded request string, does not include auth.
95 | * @return string
96 | */
97 | public function getRawRequest(){
98 | return $this->info['request']['encoded'];
99 | }
100 |
101 | /**
102 | * Get http headers returned by Mapbox
103 | * @return string
104 | */
105 | public function getHeaders(){
106 | return $this->headers;
107 | }
108 |
109 | /**
110 | * Get information on the call
111 | * @return string
112 | */
113 | public function getInfo(){
114 | return $this->info;
115 | }
116 |
117 | }
118 | ?>
119 |
--------------------------------------------------------------------------------
/php/MapboxTest.php:
--------------------------------------------------------------------------------
1 | writeToFile) {
20 | echo "\nTesting Mapbox\n";
21 | echo "========================\n";
22 | } else {
23 | if ($this->writeToFile) {
24 | //remove extant log file
25 | @ unlink($this->writeToFile);
26 | }
27 | }
28 | $this->testVersion();
29 | $this->testExt();
30 | $authenticated = $this->testConnect();
31 | if ($authenticated){
32 | $this->testGeocode();
33 | $this->testReverseGeocode();
34 | $this->testGeocodePermanent();
35 | //$this->testCountries();
36 | }
37 |
38 | if (!$this->writeToFile) {
39 | echo "========================\n\n";
40 | }
41 | }
42 |
43 |
44 | /**
45 | * Set file to log report to. Echoes to screen by default
46 | * @return void
47 | */
48 | public function setLogFile($fileName = null) {
49 | if ($fileName) {
50 | $this->writeToFile = $fileName;
51 | }
52 | }
53 |
54 | private function testGeocode() {
55 | try {
56 | $res = $this->mapbox->geocode("149 9th St, San Francisco, CA 94103");
57 | } catch (Exception $e) {
58 | $this->msg("Geocoder", false, $e->getMessage());
59 | return false;
60 | }
61 | if ($res->success()){
62 | $this->msg("Geocoder", true);
63 | } else {
64 | $this->msg("Geocoder", false);
65 | }
66 | }
67 |
68 | private function testGeocodePermanent() {
69 | try {
70 | $res = $this->mapbox->geocodePermanent("149 9th St, San Francisco, CA 94103");
71 | } catch (Exception $e) {
72 | $this->msg("Permanent Geocoder", false, $e->getMessage());
73 | return false;
74 | }
75 | if ($res->success()){
76 | $this->msg("Permanent Geocoder", true);
77 | } else {
78 | $this->msg("Permanent Geocoder", false);
79 | }
80 | }
81 |
82 | private function testReverseGeocode() {
83 | $lon = -122.143895;
84 | $lat = 37.425674;
85 | try {
86 | $res = $this->mapbox->reverseGeocode($lon, $lat);
87 | } catch (Exception $e) {
88 | $this->msg("Reverse Geocoder", false, $e->getMessage());
89 | return false;
90 | }
91 | if ($res->success()){
92 | $this->msg("Reverse Geocoder", true);
93 | } else {
94 | $this->msg("Reverse Geocoder", false);
95 | }
96 | }
97 |
98 | public function __construct($token) {
99 | if (!$token) {
100 | $this->msg("Token is required in class constructor", null);
101 | exit;
102 | }
103 | $this->mapbox = new mapbox($token);
104 | }
105 |
106 | /**
107 | * Runs a quick query to test token
108 | */
109 | private function testConnect() {
110 | $str = "Mapbox Authentication";
111 | if (file_get_contents("https://api.mapbox.com?".$this->mapbox->gettoken()) == "{\"api\":\"mapbox\"}"){
112 | $this->msg($str, true);
113 | } else {
114 | $this->msg($str, false);
115 | }
116 | return true;
117 | }
118 |
119 | /**
120 | * Confirms correct extensions (dependencies) are installed
121 | */
122 | private function testExt() {
123 | $modules = array (
124 | "SPL",
125 | "curl",
126 | "json"
127 | );
128 | $ext = array_flip(get_loaded_extensions());
129 | foreach ($modules as $module) {
130 | if ($ext[$module]) {
131 | $this->msg("PHP ".$module . " is loaded", true);
132 | } else {
133 | $this->msg("PHP ".$module . " is not loaded", false);
134 | }
135 | }
136 | }
137 |
138 | private function testVersion() {
139 | $version = explode('.', phpversion());
140 | if ((int) $version[0] >= 5) {
141 | $status = true;
142 | } else {
143 | $status = false;
144 | }
145 | $this->msg("PHP verison v5+", $status);
146 | }
147 |
148 | //writes status to stdout or to optional logfile
149 | private function msg($mesage, $status, $deets = null) {
150 | $lineLength = 40;
151 | if (is_bool($status)) {
152 | //convert to string
153 | if ($status) {
154 | $status = "OK";
155 | } else {
156 | $status = "Doh";
157 | }
158 | //color for cli
159 | if (!$this->writeToFile) {
160 | if ($status == "OK") {
161 | $status = "\033[0;32m" . $status . "\033[0m";
162 | } else {
163 | $status = "\033[0;31m" . $status . "\033[0m";
164 | }
165 | }
166 | }
167 | //fancypants alignment
168 | $message = $mesage . str_repeat(" ", $lineLength -strlen($mesage)) . $status;
169 | if ($deets) {
170 | $message .= "\t" . $deets;
171 | }
172 | $message .= "\n";
173 | if ($this->writeToFile) {
174 | $fp = fopen($this->writeToFile, 'a');
175 | fwrite($fp, $message);
176 | fclose($fp);
177 | } else {
178 | echo $message;
179 | }
180 | }
181 |
182 | }
183 | ?>
184 |
--------------------------------------------------------------------------------
/php/README.md:
--------------------------------------------------------------------------------
1 | #Introduction
2 | This is the official PHP driver for the [Mapbox API](https://www.mapbox.com/developers/api/). It is crafted with artisanal skill from native hardwoods.
3 |
4 | The PHP driver currently supports geocoding, reverse geocoding, and permanent geocoding APIs. Others follow.
5 |
6 | #PHP Specifics
7 | ##Dependencies
8 | * PHP >=5.1.2 is required.
9 | * The php5-curl module is required.
10 | * SPL is required for autoloading.
11 | * JSON is required. Some distributions as of PHP 5.5rc2 lack the previously included JSON extension due to a license conflict. Use sudo apt-get install php5-json.
12 |
13 | ##Autoloading
14 | All classes are autoloaded. Just require_once("Mapbox.php") and you're laughing.
15 |
16 | The PHP __autoload() method is deprecated; this library uses spl_autoload_register(). The Mapbox Autoload will not mess with other libraries or frameworks.
17 |
18 | #Getting Started
19 | ## Get a Mapbox Token
20 | [Register with Mapbox](https://www.mapbox.com/studio/signup/) for free access to most Mapbox services. Go to the [API Token page](https://www.mapbox.com/studio/account/tokens/) to get or create an API token.
21 |
22 | ## Test Your Integration and Environment
23 | Run test.php on the *command line*:
24 |
25 | 'php test.php [logfile]'
26 |
27 | On windows remember to use the -f switch:
28 |
29 | 'php -f test.php [logfile]'
30 |
31 | This checks your PHP install environment and performs a number of unit tests. The script takes your token as parameter one, and an optional output file as parameter two. By default the test echoes to stdout.
32 |
33 | ## Using the Driver
34 | Require the file 'Mapbox.php, and instantiate a mapbox object with the token as parameter'
35 |
36 | ```php
37 | //setup
38 | require_once('Mapbox.php');
39 | $mapbox = new Mapbox("");
40 | ```
41 | The driver creates an authenticated handle to Mapbox and configures class loading on instantiation, so be sure to always instantiate a Mapbox object first.
42 |
43 | ## Geocode Example
44 |
45 | (Remember, first create a Mapbox object as we've done above.)
46 | ```php
47 | //geocode
48 | $address = "149 9th St, San Francisco, CA 94103";
49 | $res = $mapbox->geocode($address);
50 | //view results for debugging
51 | print_r($res->getData());
52 | ```
53 |
54 | You can add the `types` and `proximity` parameters:
55 | ```php
56 | //types
57 | $types = array('region','place');
58 | $res = $mapbox->geocode($address, $types);
59 | ```
60 | or
61 |
62 | ```php
63 | //proximity
64 | $proximity = array('longitude'=>-122,'latitude'=>37);
65 | $res = $mapbox->geocode($address, "", $proximity);
66 | ```
67 | ## Reverse Geocode Example
68 |
69 | ```php
70 | //reverse geocode
71 | $longitude = -122;
72 | $latitude = 37;
73 | $res = $mapbox->reverseGeocode($longitude, $latitude);
74 | //view results for debugging
75 | print_r($res->getData());
76 | ```
77 |
78 | You can use the `types` parameter with reverse geocoding too, effectively giving you a form of point-in-polygon of different geography types:
79 | ```php
80 | //types
81 | $types = array('postcode');
82 | $res = $mapbox->reverseGeocode($longitude, $latitude, $types);
83 | ```
84 | Pro Tip: longitude always comes before latitude in parameter order, except when it doesn't.
85 |
86 | Permanent Geocoding requires specific authentication from Mapbox, so it's a different method:
87 |
88 | ```php
89 | //permanent geocode
90 | $address = "149 9th St, San Francisco, CA 94103";
91 | $res = $mapbox->geocodePermanent($address);
92 | //view results for debugging
93 | print_r($res->getData());
94 | ```
95 |
96 | Unnecessary Reminder: we use print_r() in these examples so you can review the output visually. Obviously, but worth a reminder nonetheless, you do not want to use print_r() in production.
97 |
98 | ## Working with Results
99 |
100 | The response object is an iterator, so you can iterate on it directly:
101 |
102 | ```php
103 | //iterate
104 | foreach ($res as $key => $value){
105 | //do something with each result
106 | print_r($value);
107 | }
108 | ```
109 | You can also just get the results as an array:
110 |
111 | ```php
112 | $results = $res->getData();
113 | print_r($results);
114 | ```
115 |
116 | ## Debugging and Metadata
117 |
118 | A boatload of tools are available in the driver to help you understand your request, and what the server is (or is not) returning. Putting the mapbox object in debug mode will dump a ton of metadata to stdout on each query:
119 |
120 | ```php
121 | $mapbox->debug();
122 | ```
123 |
124 | and a wealth of other metadata is available via the response object outside debug mode:
125 |
126 | ```php
127 | //was the request successful?
128 | $success = $res->success();
129 |
130 | //get result count
131 | $count = $res->getCount();
132 |
133 | //get http status code: 200, 404, etc.
134 | $code = $res->getCode();
135 |
136 | //get server headers (including rate-limit information)
137 | $headers = $res->getHeaders();
138 |
139 | //get request metadata
140 | $request - $res->getInfo();
141 |
142 | //get attribution
143 | $attribution - $res->getAttribution();
144 |
145 | ```
146 |
147 |
148 |
149 |
150 |
151 |
152 |
--------------------------------------------------------------------------------
/php/test.php:
--------------------------------------------------------------------------------
1 | [log file]\n";
12 | echo "Add your token and optional logfile as parameters to this command line script.\n\n";
13 | exit;
14 | } else {
15 | if (isset($argv[1])){
16 | $token = $argv[1];
17 | if (isset($argv[2])){$logFile = $argv[2];} else {$logFile="";}
18 | } else {
19 | echo "Token required:\n";
20 | echo "Usage: php test.php [log file]\n";
21 | exit;
22 | }
23 | }
24 |
25 | //Run tests
26 | require_once('MapboxTest.php');
27 | $mapboxTest = new mapboxTest($token);
28 | $mapboxTest->setLogFile($logFile);
29 | $mapboxTest->test();
30 |
31 |
32 |
33 |
34 |
35 | exit;
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 | ?>
44 |
--------------------------------------------------------------------------------
/python/README.md:
--------------------------------------------------------------------------------
1 | # Python 2/3 - Mapbox Geocoder
2 |
3 | `mapbox_geocode.py` takes a single query as a parameter and outputs the result.
4 |
5 | ## Requirements
6 |
7 | - Python 2/3
8 | - A Mapbox access token with geocoding capabilities
9 |
10 | ## Usage
11 |
12 | Command line:
13 | ```
14 | MapboxAccessToken=YOUR_ACCESS_TOKEN python ./mapbox_geocode.py '1600 Pennsylvania Ave Washington, DC'
15 | ```
16 |
17 | Programmatic:
18 | ```
19 | >>> from mapbox_geocode import geocode
20 | >>> geocode('YOUR_ACCESS_TOKEN', '1600 Pennsylvania Ave Washington, DC')
21 | {u'attribution': u'\xa9 2015 Mapbox and its suppliers. All rights reserved. Use of this data is subject to the Mapbox Terms of Service. (https://www.mapbox.com/about/maps/)', u'query': [u'1600', u'pennsylvania', u'ave', u'washington', u'dc'], u'type': u'FeatureCollection', u'features': [{u'center': [-77.036698, 38.897102], u'geometry': {u'type': u'Point', u'coordinates': [-77.036698, 38.897102]}, u'text': u'Pennsylvania Ave NW', u'properties': {}, u'bbox': [-77.05781199999998, 38.89252299999999, -77.01844799999999, [...]
22 | ```
23 |
24 |
--------------------------------------------------------------------------------
/python/mapbox_geocode.py:
--------------------------------------------------------------------------------
1 | import __future__
2 | import os, sys, json
3 | try:
4 | # python 3
5 | from urllib.request import urlopen as urlopen
6 | from urllib.parse import quote_plus as quote_plus
7 | except:
8 | # python 2
9 | from urllib import quote_plus as quote_plus
10 | from urllib2 import urlopen as urlopen
11 |
12 | def geocode(mapbox_access_token, query):
13 | """
14 | Submit a geocoding query to Mapbox's geocoder.
15 |
16 | Args:
17 | mapbox_access_token (str): valid Mapbox access token with geocoding permissions
18 | query (str): input text to geocode
19 | """
20 | resp = urlopen('https://api.tiles.mapbox.com/geocoding/v5/mapbox.places/{query}.json?access_token={token}'.format(query=quote_plus(query), token=mapbox_access_token))
21 | return json.loads(resp.read().decode('utf-8'))
22 |
23 | if __name__ == '__main__':
24 | token = os.environ.get('MapboxAccessToken', False)
25 | if not token:
26 | print('environment variable MapboxAccessToken must be set')
27 | sys.exit(1)
28 |
29 | # geocode
30 | result = geocode(token, sys.argv[1])
31 |
32 | # print result
33 | print(json.dumps(result, indent=2))
34 |
--------------------------------------------------------------------------------
/test/sample.txt:
--------------------------------------------------------------------------------
1 | -94.945599,29.699342,1034 Bay Oaks Harbor Dr Baytown
2 | -96.354816,30.63545,3515 S College Ave Bryan
3 | -81.627944,41.009117,1058 Wooster Rd W Barberton
4 | -118.189003,33.928799,11111 Harris Ave Lynwood
5 | -84.60042,33.858322,1676 Mulkey Rd SW Austell
6 | -97.738279,30.210142,4109 Todd Ln Austin
7 | -95.314282,32.345561,411 S Glenwood Blvd Tyler
8 | -75.606636,38.362255,560 Riverside Dr Salisbury
9 | -122.700635,45.525459,2445 NW Westover Rd Portland
10 | -72.744322,41.759584,12 Lasalle Rd Hartford
11 | -122.32571,47.609773,1011 Boren Ave Seattle
12 | -77.795722,35.938927,348 S Washington St Rocky Mount
13 | -118.563551,34.388603,25736 Player Dr Unit R14 Valencia
14 | -73.633198,40.723365,520 Franklin Ave Garden City
15 | -99.919886,34.682098,120 N 2nd St Hollis
16 | -90.651754,38.515006,17290 Hilltop Ridge Dr Eureka
17 | -104.794483,38.796845,2835 Janitell Rd Colorado Springs
18 | -76.183985,39.523655,939 Beards Hill Rd Aberdeen
19 | -80.143829,25.794232,1930 Bay Rd Miami Beach
20 | -83.381009,33.940548,100 Pinecrest Dr Athens
21 | -87.63238,41.888633,325 N La Salle Drive Suite 450 Chicago
22 | -121.930755,37.345121,1179 Sherwood Ave San Jose
23 | -83.764063,32.878563,6501 Peake Rd Macon
24 | -97.772737,30.221144,4315 S 1st St Austin
25 | -89.889491,35.105921,5180 Park Ave Memphis
26 | -118.263817,33.831524,650 E Carson St Carson
27 | -79.083975,38.173681,1107 Pine Glen Rd Staunton
28 | -84.038382,33.969805,1033 Colony Creek Dr Lawrenceville
29 | -77.51494,38.806271,10342 Battleview Pkwy Manassas
30 | -83.018873,40.075739,5540 N High St Columbus
31 | -90.404707,38.631346,5 Blaytonn Ln Saint Louis
32 | -75.406363,39.870275,5002 Jefferson Dr Brookhaven
33 | -80.254585,25.804418,3630 NW North River Dr Miami
34 | -73.983334,40.781406,305 W End Ave New York
35 | -74.105028,40.019367,10 Farragut Dr Brick
36 | -84.554513,39.587299,171 Main St West Elkton
37 | -78.430519,40.44567,3134 Old 6th Ave N Duncansville
38 | -112.125964,33.492199,3107 W Clarendon Ave Phoenix
39 | -73.992757,40.698062,92 Henry St Brooklyn
40 | -81.903992,41.476213,25735 1st St Westlake
41 | -84.147334,33.440193,280 Griffin St Mcdonough
42 | -116.37109,33.722113,74020 Alessandro Dr Palm Desert
43 | -74.940561,40.233958,2700 S Eagle Rd Newtown
44 | -81.314896,28.694089,1255 Belle Ave Winter Springs
45 | -111.864641,33.248237,"3920 South Alma School Road, Suite 5 Chandler"
46 | -86.478095,36.375861,180 Belvedere Dr N Gallatin
47 | -86.739977,36.201015,2901 Gallatin Pike Nashville
48 | -85.53993,40.482588,4853 S 700 E Marion
49 | -73.540651,40.66051,2326 Merrick Rd Merrick
50 | -77.417898,37.553858,2305 Whitcomb St Richmond
51 | -81.344373,35.238642,219 S Battleground Ave Kings Mountain
52 | -79.900733,39.936493,188 Murphy Rd Adah
53 | -117.098407,32.677664,943 Highland Ave National City
54 | -157.878883,21.323581,1451 Kalani St Honolulu
55 | -74.122038,40.906138,92 Grove St Elmwood Park
56 | -73.959297,40.814701,3165 Broadway New York
57 | -115.25135,36.219406,3250 N Tenaya Way Suite 103 Las Vegas
58 | -122.182499,37.747822,930 88th Ave Oakland
59 | -90.253711,38.774726,11026 Linnell Dr Saint Louis
60 | -73.807487,42.764911,1050 Troy Schenectady Rd Latham
61 | -77.659265,43.143934,67 Stanton St Rochester
62 | -83.977559,43.46278,3165 Cabaret Trl S Saginaw
63 | -97.654518,35.249465,3528 W Fox Ln Newcastle
64 | -74.032392,41.040068,25 W Grand Ave Montvale
65 | -81.232462,35.782027,1234 Turtle Dove Rd Conover
66 | -92.116972,43.370952,214 S Elm St Cresco
67 | -117.267375,34.471002,18215 Bear Valley Rd Hesperia
68 | -77.0326,38.905998,1100 New York Ave NW Washington
69 | -93.272045,45.103132,7449 E River Rd Fridley
70 | -80.142473,26.010388,1750 S Young Cir Hollywood
71 | -76.501811,42.45169,214 W Lincoln St Ithaca
72 | -74.7483,40.211284,1 Swan St Trenton
73 | -157.842197,21.301301,1296 S Beretania St Honolulu
74 | -71.060821,42.35551,8 Winter St Boston
75 | -73.324699,40.8773,75 Larkfield Rd East Northport
76 |
--------------------------------------------------------------------------------
/test/test.sh:
--------------------------------------------------------------------------------
1 | set -eu
2 |
3 | function valid_response() {
4 | RESULT_LENGTH="`echo "$1" | jq length`"
5 | if [ "$?" -eq 0 ] && [ "$RESULT_LENGTH" -gt 0 ]; then
6 | VALID=0
7 | else
8 | VALID=1
9 | fi
10 | }
11 |
12 | function valid_batch_response() {
13 | INPUT_FILE=$1
14 | DIRECTORY=$2
15 | TOTAL_RESULTS=0
16 | for f in ${DIRECTORY}/*.json; do
17 | RESULT_LENGTH="`cat $f | jq length`"
18 | TOTAL_RESULTS=$(($TOTAL_RESULTS + $RESULT_LENGTH))
19 | done
20 | if [ "$?" -ne 0 ] || [ "$TOTAL_RESULTS" -eq 0 ] || [ "$TOTAL_RESULTS" -ne "`wc -l $INPUT_FILE | awk '{print $1}'`" ]; then
21 | VALID=1
22 | else
23 | VALID=0
24 | fi
25 | }
26 |
27 | function cleanslate() {
28 | mkdir -p $OUT_DIR || true
29 | rm -f $OUT_DIR/* || true
30 |
31 | rm -rf $(dirname $0)/../node/node_modules/
32 | rm -rf $(dirname $0)/../batch_node/node_modules/
33 | rm -rf $(dirname $0)/../python/.virtualenv_*/
34 | rm -rf $(dirname $0)/../batch_python/.virtualenv_*/
35 | }
36 |
37 |
38 | MapboxAccessToken=${MapboxAccessToken:-""}
39 | if [ -z "$MapboxAccessToken" ]; then
40 | echo "environment variable MapboxAccessToken must be set"
41 | exit 1
42 | fi
43 |
44 | OUT_DIR="$(dirname $0)/out"
45 | SAMPLE_FILE="$(dirname $0)/sample_`date +"%s"`.txt"
46 |
47 | cleanslate
48 |
49 | CODE=0
50 | QUERY='1714 14th St NW Washington, DC 20009'
51 | PYTHONS='python2.7 python3'
52 |
53 | # ----- setup -----
54 |
55 | # python
56 | for dir in 'python' 'batch_python'; do
57 | for PYTHONX in $PYTHONS; do
58 | virtualenv -p `which $PYTHONX` $(dirname $0)/../${dir}/.virtualenv_${PYTHONX}
59 | if [ -e "$(dirname $0)/../${dir}/requirements.txt" ]; then
60 | $(dirname $0)/../${dir}/.virtualenv_${PYTHONX}/bin/pip install -r $(dirname $0)/../${dir}/requirements.txt
61 | fi
62 | done
63 | done
64 |
65 | # node
66 | for dir in 'node' 'batch_node'; do
67 | if [ -e "$(dirname $0)/../${dir}/package.json" ]; then
68 | cd "$(dirname $0)/../${dir}/" && npm install && cd ..
69 | fi
70 | done
71 |
72 | # ----- basic geocoding -----
73 |
74 | # python
75 | for PYTHONX in $PYTHONS; do
76 | echo "# testing basic geocoding / $PYTHONX"
77 | RESPONSE="`$(dirname $0)/../python/.virtualenv_${PYTHONX}/bin/python $(dirname $0)/../python/mapbox_geocode.py "$QUERY"`"
78 | valid_response "$RESPONSE"
79 | if [ "$VALID" -ne 0 ]; then
80 | echo "not ok: python/mapbox_geocode.py using $PYTHONX"
81 | CODE=1
82 | else
83 | echo "ok: python/mapbox_geocode.py using $PYTHONX"
84 | fi
85 | done
86 |
87 | # node
88 | echo "# testing basic geocoding / node"
89 | RESPONSE="`node $(dirname $0)/../node/mapbox-geocode.js "$QUERY"`"
90 | valid_response "$RESPONSE"
91 | if [ "$VALID" -ne 0 ]; then
92 | echo "not ok: node/mapbox-geocode.js"
93 | CODE=1
94 | else
95 | echo "ok: node/mapbox-geocode.js"
96 | fi
97 |
98 | # bash
99 | echo "# testing basic geocoding / bash"
100 | RESPONSE="`bash $(dirname $0)/../bash/mapbox_geocode.sh "$QUERY"`"
101 | valid_response "$RESPONSE"
102 | if [ "$VALID" -ne 0 ]; then
103 | echo "not ok: bash/mapbox_geocode.sh"
104 | CODE=1
105 | else
106 | echo "ok: bash/mapbox_geocode.sh"
107 | fi
108 |
109 | # ----- batch geocoding -----
110 |
111 | SHUF="`which shuf || true`"
112 | if [ -z "$SHUF" ]; then
113 | SHUF="`which gshuf`"
114 | fi
115 | $SHUF $(dirname $0)/sample.txt > $SAMPLE_FILE
116 |
117 | # python
118 | for PYTHONX in $PYTHONS; do
119 | echo "# testing batch geocoding / $PYTHONX"
120 | $(dirname $0)/../batch_python/.virtualenv_${PYTHONX}/bin/python $(dirname $0)/../batch_python/mapbox_batch.py $SAMPLE_FILE $OUT_DIR
121 | valid_batch_response $SAMPLE_FILE $OUT_DIR
122 | if [ "$VALID" -ne 0 ]; then
123 | echo "not ok: batch_python/mapbox_batch.py using $PYTHONX"
124 | CODE=1
125 | else
126 | echo "ok: batch_python/mapbox_batch.py using $PYTHONX"
127 | fi
128 | rm $OUT_DIR/* || true
129 | done
130 |
131 | # node
132 | echo "# testing batch geocoding / node"
133 | node $(dirname $0)/../batch_node/mapbox-batch.js $SAMPLE_FILE $OUT_DIR
134 | valid_batch_response $SAMPLE_FILE $OUT_DIR
135 | if [ "$VALID" -ne 0 ]; then
136 | echo "not ok: batch_node/mapbox-batch.js"
137 | CODE=1
138 | else
139 | echo "ok: batch_node/mapbox-batch.js"
140 | fi
141 | rm $OUT_DIR/* || true
142 |
143 | # ----- teardown -----
144 |
145 | cleanslate
146 |
147 | rm -rf $OUT_DIR $SAMPLE_FILE
148 | rm -rf $(dirname $0)/sample_*.txt
149 |
150 | exit $CODE
151 |
--------------------------------------------------------------------------------