function downloadFromOverpass (
  queryName,
  overpassConfig,
  filename,
  overpassDownloadCallback
) {
  let query = '[out:json][timeout:60];('
  if (overpassConfig.way) {
    query += 'way'
  } else {
    query += 'relation'
  }

  const queryKeys = Object.keys(overpassConfig)

  for (let i = queryKeys.length - 1; i >= 0; i--) {
    const k = queryKeys[i]
    if (k === 'way') continue
    const v = overpassConfig[k]

    query += '["' + k + '"="' + v + '"]'
  }

  query += ';);out body;>;out meta qt;'

  // query-overpass sometimes makes duplicate callbacks, so keep track of the callbacks and
  // only do a next action once.
  let curOverpassQueryAttempt = 0
  const overpassAttempts = {}

  asynclib.auto({
    fetchFromOverpassIfNeeded: function (cb) {
      console.log('downloading from overpass')
      fetchIfNeeded(filename, overpassDownloadCallback, cb, function () {
        const overpassResponseHandler = function (err, data, overpassAttempt) {
          if (overpassAttempts[overpassAttempt]) {
            // Skip duplicate callback
            return
          }
          overpassAttempts[overpassAttempt] = true
          if (err) {
            console.log(err)
            console.log('Increasing overpass request gap')
            curRequestGap *= 2
            makeQuery()
          } else {
            console.log('Success, decreasing overpass request gap')
            curRequestGap = Math.max(minRequestGap, curRequestGap / 2)
            cb(null, data)
          }
        }
        const makeQuery = function () {
          console.log('waiting ' + curRequestGap + ' seconds')
          setTimeout(function () {
            curOverpassQueryAttempt++
            overpass(
              query,
              (err, data) => overpassResponseHandler(err, data, curOverpassQueryAttempt),
              { flatProperties: true }
            )
          }, curRequestGap * 1000)
        }
        makeQuery()
      })
    },
    validateOverpassResult: ['fetchFromOverpassIfNeeded', function (results, cb) {
      const data = results.fetchFromOverpassIfNeeded
      if (!data.features) {
        const err = new Error(`Invalid geojson from overpass for query: ${queryName}`)
        return cb(err)
      }
      if (data.features.length === 0) {
        console.error('No data for the following query:')
        console.error(query)
        console.error('To read more about this error, please visit https://git.io/vxKQL')
        return cb(new Error('No data found for from overpass query'))
      }
      cb()
    }],
    saveSingleMultiPolygon: ['validateOverpassResult', function (results, cb) {
      const data = results.fetchFromOverpassIfNeeded
      let combined

      // union all multi-polygons / polygons into one
      for (let i = data.features.length - 1; i >= 0; i--) {
        const curOsmGeom = data.features[i].geometry
        const curOsmProps = data.features[i].properties
        if (
          (curOsmGeom.type === 'Polygon' || curOsmGeom.type === 'MultiPolygon') &&
          curOsmProps.type === 'boundary' // need to make sure enclaves aren't unioned
        ) {
          console.log('combining border')
          let errors = geojsonhint.hint(curOsmGeom)
          if (errors && errors.length > 0) {
            const stringifiedGeojson = JSON.stringify(curOsmGeom, null, 2)
            errors = geojsonhint.hint(stringifiedGeojson)
            console.error('Invalid geojson received in Overpass Result')
            console.error('Overpass query: ' + query)
            const problemFilename = `${queryName}_convert_to_geom_error.json`
            fs.writeFileSync(problemFilename, stringifiedGeojson)
            console.error('saved problem file to ' + problemFilename)
            console.error('To read more about this error, please visit https://git.io/vxKQq')
            return cb(errors)
          }
          let curGeom
          try {
            curGeom = geoJsonToGeom(curOsmGeom)
          } catch (e) {
            console.error('error converting overpass result to geojson')
            console.error(e)

            fs.writeFileSync(
              `${queryName}_convert_to_geom_error-all-features.json`,
              JSON.stringify(data)
            )
            return cb(e)
          }
          if (!combined) {
            combined = curGeom
          } else {
            combined = debugGeo('union', curGeom, combined)
          }
        }
      }
      try {
        fs.writeFile(filename, geomToGeoJsonString(combined), cb)
      } catch (e) {
        console.error('error writing combined border to geojson')
        fs.writeFileSync(
          queryName + '_combined_border_convert_to_geom_error.json',
          JSON.stringify(data)
        )
        return cb(e)
      }
    }]
  }, overpassDownloadCallback)
}
Download something from overpass and convert it into GeoJSON.

@param  {string} queryName  Name of the query (for debugging purposes)
@param  {object} overpassConfig Config used to build overpass query
@param  {string} filename  Filename to save result to
@param  {function} overpassDownloadCallback  The callback to call when done