├── .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 | [![Build Status](https://travis-ci.org/mapbox/geocoding-example.svg?branch=master)](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 | --------------------------------------------------------------------------------