text: from osmnx import settings
  import requests
  from osmnx.downloader import get_from_cache, get_http_headers, get_pause_duration, save_to_cache
  from osmnx.utils import make_str, log
  import time
  import re
  import networkx as nx
  import osmnx as ox
  
  def overpass_request(data, pause_duration=None, timeout=180, error_pause_duration=None):
      """
      Send a request to the Overpass API via HTTP POST and return the JSON
      response.
      Parameters
      ----------
      data : dict or OrderedDict
          key-value pairs of parameters to post to the API
      pause_duration : int
          how long to pause in seconds before requests, if None, will query API
          status endpoint to find when next slot is available
      timeout : int
          the timeout interval for the requests library
      error_pause_duration : int
          how long to pause in seconds before re-trying requests if error
      Returns
      -------
      dict
      """
  
      # define the Overpass API URL, then construct a GET-style URL as a string to
      # hash to look up/save to cache
      url = settings.overpass_endpoint.rstrip('/') + '/interpreter'
      prepared_url = requests.Request('GET', url, params=data).prepare().url
      cached_response_json = get_from_cache(prepared_url)
  
      if cached_response_json is not None:
          # found this request in the cache, just return it instead of making a
          # new HTTP call
          return cached_response_json
  
      else:
          # if this URL is not already in the cache, pause, then request it
          if pause_duration is None:
              this_pause_duration = get_pause_duration()
          log('Pausing {:,.2f} seconds before making API POST request'.format(this_pause_duration))
          time.sleep(this_pause_duration)
          start_time = time.time()
          log('Posting to {} with timeout={}, "{}"'.format(url, timeout, data))
          response = requests.post(url, data=data, timeout=timeout, headers=get_http_headers())
  
          # get the response size and the domain, log result
          size_kb = len(response.content) / 1000.
          domain = re.findall(r'(?s)//(.*?)/', url)[0]
          log('Downloaded {:,.1f}KB from {} in {:,.2f} seconds'.format(size_kb, domain, time.time() - start_time))
  
          try:
              response_json = response.json()
        #      if 'remark' in response_json:
        #          log('Server remark: "{}"'.format(response_json['remark'], level=lg.WARNING))
              save_to_cache(prepared_url, response_json)
          except Exception:
              # 429 is 'too many requests' and 504 is 'gateway timeout' from server
              # overload - handle these errors by recursively calling
              # overpass_request until we get a valid response
              if response.status_code in [429, 504]:
                  # pause for error_pause_duration seconds before re-trying request
                  if error_pause_duration is None:
                      error_pause_duration = get_pause_duration()
                  response_json = overpass_request(data=data, pause_duration=pause_duration, timeout=timeout)
  
              # else, this was an unhandled status_code, throw an exception
              else:
                  log('Server at {} returned status code {} and no JSON data'.format(domain, response.status_code),
                      level=lg.ERROR)
                  raise Exception(
                      'Server returned no JSON data.\n{} {}\n{}'.format(response, response.reason, response.text))
  
          return response_json
  
  
  def get_node(element):
      """
      Convert an OSM node element into the format for a networkx node.
  
      Parameters
      ----------
      element : dict
          an OSM node element
  
      Returns
      -------
      dict
      """
      useful_tags_node = ['ref', 'highway', 'route_ref']
      node = {}
      node['y'] = element['lat']
      node['x'] = element['lon']
      node['osmid'] = element['id']
  
      if 'tags' in element:
          for useful_tag in useful_tags_node:
              if useful_tag in element['tags']:
                  node[useful_tag] = element['tags'][useful_tag]
      return node
  
  def parse_osm_nodes_paths(osm_data):
      """
      Construct dicts of nodes and paths with key=osmid and value=dict of
      attributes.
  
      Parameters
      ----------
      osm_data : dict
          JSON response from from the Overpass API
  
      Returns
      -------
      nodes, paths : tuple
      """
  
      nodes = {}
      paths = {}
      for element in osm_data['elements']:
          if element['type'] == 'node':
              key = element['id']
              nodes[key] = get_node(element)
  
          elif element['type'] == 'way': #osm calls network paths 'ways'
              key = element['id']
              paths[key] = ox.get_path(element)
  
      return nodes, paths
  
  def create_graph(response_jsons, name='unnamed', retain_all=True, bidirectional=False):
      """
      Create a networkx graph from Overpass API HTTP response objects.
  
      Parameters
      ----------
      response_jsons : list
          list of dicts of JSON responses from from the Overpass API
      name : string
          the name of the graph
      retain_all : bool
          if True, return the entire graph even if it is not connected
      bidirectional : bool
          if True, create bidirectional edges for one-way streets
  
      Returns
      -------
      networkx multidigraph
      """
  
      log('Creating networkx graph from downloaded OSM data...')
      start_time = time.time()
  
      # make sure we got data back from the server requests
     # elements = []
      # for response_json in response_jsons:
      #elements.extend(response_json['elements'])
      #if len(elements) < 1:
     #     raise EmptyOverpassResponse('There are no data elements in the response JSON objects')
  
      # create the graph as a MultiDiGraph and set the original CRS to default_crs
      G = nx.MultiDiGraph(name=name, crs=settings.default_crs)
  
      # extract nodes and paths from the downloaded osm data
      nodes = {}
      paths = {}
      # for osm_data in response_jsons:
      nodes_temp, paths_temp = parse_osm_nodes_paths(response_jsons)
      for key, value in nodes_temp.items():
          nodes[key] = value
      for key, value in paths_temp.items():
          paths[key] = value
  
      # add each osm node to the graph
      for node, data in nodes.items():
          G.add_node(node, **data)
  
      # add each osm way (aka, path) to the graph
      G = ox.add_paths(G, paths, bidirectional=bidirectional)
  
      # retain only the largest connected component, if caller did not
      # set retain_all=True
      if not retain_all:
          G = get_largest_component(G)
  
      log('Created graph with {:,} nodes and {:,} edges in {:,.2f} seconds'.format(len(list(G.nodes())), len(list(G.edges())), time.time()-start_time))
  
      # add length (great circle distance between nodes) attribute to each edge to
      # use as weight
      if len(G.edges) > 0:
          G = ox.add_edge_lengths(G)
  
      return G
  