├── .editorconfig ├── .github └── workflows │ └── test.yml ├── .gitignore ├── example.js ├── index.js ├── license.md ├── package.json ├── readme.md └── test.js /.editorconfig: -------------------------------------------------------------------------------- 1 | # editorconfig.org 2 | root = true 3 | 4 | [*] 5 | end_of_line = lf 6 | charset = utf-8 7 | trim_trailing_whitespace = true 8 | insert_final_newline = true 9 | 10 | # Use tabs in JavaScript and JSON. 11 | [**.{js, json}] 12 | indent_style = tab 13 | indent_size = 4 14 | 15 | # Use spaces in YAML. 16 | [**.{yml,yaml}] 17 | indent_style = spaces 18 | indent_size = 2 19 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: test 2 | 3 | on: 4 | push: 5 | branches: 6 | - '*' 7 | pull_request: 8 | branches: 9 | - '*' 10 | 11 | jobs: 12 | test: 13 | runs-on: ubuntu-latest 14 | strategy: 15 | matrix: 16 | node-version: ['8', '10', '12', '14', '16'] 17 | 18 | steps: 19 | - name: checkout 20 | uses: actions/checkout@v2 21 | - name: setup Node v${{ matrix.node-version }} 22 | uses: actions/setup-node@v1 23 | with: 24 | node-version: ${{ matrix.node-version }} 25 | - run: npm install 26 | 27 | - run: npm test 28 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | Thumbs.db 3 | 4 | .nvm-version 5 | node_modules 6 | npm-debug.log 7 | 8 | /package-lock.json 9 | -------------------------------------------------------------------------------- /example.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const queryOverpass = require('.') 4 | 5 | queryOverpass(` 6 | [out:json][timeout:25]; 7 | node(3378340880); 8 | out body; 9 | `) 10 | .then(console.log) 11 | .catch(console.error) 12 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const retry = require('p-retry') 4 | const Promise = require('pinkie-promise') 5 | const {fetch} = require('fetch-ponyfill')({Promise}) 6 | const {stringify} = require('query-string') 7 | 8 | const defaults = { 9 | fetchMode: null, 10 | endpoint: 'https://overpass-api.de/api/interpreter', 11 | retryOpts: {minTimeout: 500, retries: 3} 12 | } 13 | 14 | const queryOverpass = (query, opt = {}) => { 15 | opt = Object.assign({}, defaults, opt) 16 | 17 | if ('string' !== typeof opt.endpoint) { 18 | throw new Error('opt.endpoint must be a string') 19 | } 20 | const url = opt.endpoint + '?' + stringify({data: query}) 21 | const fetchOpts = { 22 | redirect: 'follow', 23 | headers: { 24 | accept: 'application/json', 25 | 'user-agent': 'http://github.com/derhuerst/vbb-osm-relations' 26 | } 27 | } 28 | if (opt.fetchMode !== null) fetchOpts.mode = opt.fetchMode 29 | 30 | const attempt = () => { 31 | return fetch(url, fetchOpts) 32 | .then((res) => { 33 | if (!res.ok) { 34 | const err = new Error(res.statusText) 35 | err.statusCode = res.status 36 | err.reponseBody = null 37 | if (!res.headers.has('content-length') || parseInt(res.headers.get('content-length')) > 1024) { 38 | throw err 39 | } 40 | return res.text() 41 | .then((body) => { 42 | err.reponseBody = body 43 | throw err 44 | }, () => { 45 | throw err 46 | }) 47 | } 48 | return res.json() 49 | }) 50 | .then((data) => { 51 | if (!data || !Array.isArray(data.elements)) { 52 | const err = new Error('invalid response') 53 | err.responseBody = data 54 | throw err 55 | } 56 | return data.elements 57 | }) 58 | } 59 | 60 | const retryOpts = Object.assign({}, defaults.retryOpts, opt.retryOpts || {}) 61 | return retry(attempt, retryOpts) 62 | } 63 | 64 | module.exports = queryOverpass 65 | -------------------------------------------------------------------------------- /license.md: -------------------------------------------------------------------------------- 1 | Copyright (c) 2018, Jannis R 2 | 3 | Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. 4 | 5 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 6 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@derhuerst/query-overpass", 3 | "description": "Query the OpenStreetMap Overpass API.", 4 | "version": "2.0.1", 5 | "main": "index.js", 6 | "files": [ 7 | "index.js" 8 | ], 9 | "keywords": [ 10 | "osm", 11 | "openstreetmap", 12 | "overpass", 13 | "api", 14 | "client" 15 | ], 16 | "author": "Jannis R ", 17 | "homepage": "https://github.com/derhuerst/query-overpass", 18 | "repository": "derhuerst/query-overpass", 19 | "bugs": "https://github.com/derhuerst/query-overpass/issues", 20 | "license": "ISC", 21 | "engines": { 22 | "node": ">=8" 23 | }, 24 | "dependencies": { 25 | "fetch-ponyfill": "^6.0.0", 26 | "p-retry": "^4.1.0", 27 | "pinkie-promise": "^2.0.1", 28 | "query-string": "^6.0.0" 29 | }, 30 | "devDependencies": { 31 | "tap-min": "^2.0.0", 32 | "tape": "^5.0.0" 33 | }, 34 | "scripts": { 35 | "test": "node test.js | tap-min", 36 | "prepublishOnly": "npm test" 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # query-overpass 2 | 3 | **Query the [OpenStreetMap](https://www.openstreetmap.org/) [Overpass](https://overpass-api.de) API.** Yet another implementation, because it 4 | 5 | - is isomorphic (works in the browser) 6 | - is lightweight (doesn't contain any sophisticated parser) 7 | - uses [`p-retry`](https://github.com/sindresorhus/p-retry) to resend requests 8 | 9 | Other alternatives for you to evaluate: 10 | 11 | - [`query-overpass`](https://github.com/perliedman/query-overpass) and a fork [`@sriharithalla/query-overpass`](https://github.com/SrihariThalla/query-overpass) 12 | - [`idris-overpass`](https://github.com/idris-maps/idris-overpass) and [`idris-overpass-browser`](https://github.com/idris-maps/idris-overpass-browser) 13 | 14 | [![npm version](https://img.shields.io/npm/v/@derhuerst/query-overpass.svg)](https://www.npmjs.com/package/@derhuerst/query-overpass) 15 | ![ISC-licensed](https://img.shields.io/github/license/derhuerst/query-overpass.svg) 16 | [![support me via GitHub Sponsors](https://img.shields.io/badge/support%20me-donate-fa7664.svg)](https://github.com/sponsors/derhuerst) 17 | [![chat with me on Twitter](https://img.shields.io/badge/chat%20with%20me-on%20Twitter-1da1f2.svg)](https://twitter.com/derhuerst) 18 | 19 | 20 | ## Installing 21 | 22 | ```shell 23 | npm install @derhuerst/query-overpass 24 | ``` 25 | 26 | 27 | ## Usage 28 | 29 | Pass in a string of [Overpass QL](http://wiki.openstreetmap.org/wiki/Overpass_API/Overpass_QL). Returns a [`Promise`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/promise) that will resolve with an array of elements. 30 | 31 | ```js 32 | const queryOverpass = require('@derhuerst/query-overpass') 33 | 34 | queryOverpass(` 35 | [out:json][timeout:25]; 36 | node(3378340880); 37 | out body; 38 | `) 39 | .then(console.log) 40 | .catch(console.error) 41 | ``` 42 | 43 | ```js 44 | [ { 45 | type: 'node', 46 | id: 3378340880, 47 | lat: 52.5145076, 48 | lon: 13.35011, 49 | tags: { 50 | artist: 'Friedrich Drake', 51 | artwork_type: 'statue', 52 | colour: 'gold', 53 | height: '66.9', 54 | image: 'https://upload.wikimedia.org/wikipedia/commons/2/2a/Victoria_Goldelse_Siegessaeule_Berlin.jpg', 55 | min_height: '58.6', 56 | name: 'Viktoria', 57 | 'name:de': 'Siegessäule', 58 | 'name:ru': 'Колонна победы', 59 | 'name:zh': '勝利女神柱', 60 | reg_name: 'Goldelse', 61 | tourism: 'artwork', 62 | wheelchair: 'no' 63 | } 64 | } ] 65 | ``` 66 | 67 | *Note:* The Overpass API *does not* support [extended Overpass Turbo queries](https://wiki.openstreetmap.org/wiki/Overpass_turbo/Extended_Overpass_Turbo_Queries), as they are an [Overpass Turbo](https://overpass-turbo.eu)-specific feature. 68 | 69 | To use make requests from a web site, enable [CORS](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS) using the `fetchMode` option: 70 | 71 | ```js 72 | queryOverpass(query, {fetchMode: 'cors'}) 73 | ``` 74 | 75 | To use `@derhuerst/query-overpass` with a custom Overpass API endpoint, use the `endpoint` option: 76 | 77 | ```js 78 | queryOverpass(query, {endpoint: 'https://overpass.example.org'}) 79 | ``` 80 | 81 | 82 | ## Related 83 | 84 | - [`osm-build-query`](https://github.com/csbrandt/osm-build-query) – Build a query for the OSM Overpass API that retrieves all elements within the bounding feature. 85 | - [`osm-flatten-relation`](https://github.com/derhuerst/osm-flatten-relation) – Resolve an OpenStreetMap relation recursively. 86 | 87 | 88 | ## Contributing 89 | 90 | If you have a question or have difficulties using `query-overpass`, please double-check your code and setup first. If you think you have found a bug or want to propose a feature, refer to [the issues page](https://github.com/derhuerst/query-overpass/issues). 91 | -------------------------------------------------------------------------------- /test.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const test = require('tape') 4 | 5 | const queryOverpass = require('.') 6 | 7 | const south = 52.45 8 | const west = 13.35 9 | const north = 52.5 10 | const east = 13.4 11 | const bbox = [south, west, north, east].join(',') 12 | const query = ` 13 | [out:json][timeout:25]; 14 | node["railway"="subway_entrance"](${bbox}); 15 | out body; 16 | ` 17 | 18 | test('queryOverpass', (t) => { 19 | queryOverpass(query) 20 | .then((elements) => { 21 | t.ok(Array.isArray(elements)) 22 | t.ok(elements.length > 0) 23 | for (let el of elements) { 24 | t.ok(el) 25 | t.equal(typeof el.type, 'string') 26 | t.equal(typeof el.id, 'number') 27 | t.equal(typeof el.lat, 'number') 28 | t.equal(typeof el.lon, 'number') 29 | if (el.tags) { 30 | t.notOk(Array.isArray(el.tags)) 31 | t.equal(typeof el.tags, 'object') 32 | } 33 | } 34 | t.end() 35 | }) 36 | .catch(t.ifError) 37 | }) 38 | 39 | const UNREACHABLE = 'http://127.0.0.1:1' 40 | test('custom endpoint fails', (t) => { 41 | t.plan(1) 42 | queryOverpass(query, { 43 | endpoint: UNREACHABLE, 44 | retryOpts: {retries: 0} 45 | }) 46 | .catch(t.ok) 47 | }) 48 | --------------------------------------------------------------------------------