docs: def save_graph_osm(G, node_tags=settings.osm_xml_node_tags,
                     node_attrs=settings.osm_xml_node_attrs,
                     edge_tags=settings.osm_xml_way_tags,
                     edge_attrs=settings.osm_xml_way_attrs,
                     oneway=True, filename='graph.osm',
                     folder=None):
      """
      Save a graph as an OSM XML formatted file. NOTE: for very large
      networks this method can take upwards of 30+ minutes to finish.
  
      Parameters
      __________
      G : networkx multidigraph or multigraph
      filename : string
          the name of the osm file (including file extension)
      folder : string
          the folder to contain the file, if None, use default data folder
  
      Returns
      -------
      None
      """
      start_time = time.time()
      if folder is None:
          folder = settings.data_folder
  
      # create a copy to convert all the node/edge attribute values to string
      G_save = G.copy()
  
      gdf_nodes, gdf_edges = graph_to_gdfs(
          G_save, node_geometry=False, fill_edge_geometry=False)
  
      # rename columns per osm specification
      gdf_nodes.rename(
          columns={'osmid': 'id', 'x': 'lon', 'y': 'lat'}, inplace=True)
      if 'uniqueid' in gdf_edges.columns:
          gdf_edges = gdf_edges.rename(columns={'uniqueid': 'id'})
      else:
          gdf_edges = gdf_edges.reset_index().rename(columns={'index': 'id'})
  
      # add default values for required attributes
      for table in [gdf_nodes, gdf_edges]:
          table['uid'] = '1'
          table['user'] = 'osmnx'
          table['version'] = '1'
          table['changeset'] = '1'
          table['timestamp'] = '2017-01-01T00:00:00Z'
  
      # convert all datatypes to str
      nodes = gdf_nodes.applymap(str)
      edges = gdf_edges.applymap(str)
  
      # misc. string replacements to meet OSM XML spec
      if 'oneway' in edges.columns:
          edges.loc[:, 'oneway'] = oneway
          edges.loc[:, 'oneway'] = edges['oneway'].astype(str)
          edges.loc[:, 'oneway'] = edges['oneway'].str.replace(
              'False', 'no').replace('True', 'yes')
  
      # initialize XML tree with an OSM root element
      root = etree.Element('osm')
  
      # append nodes to the XML tree
      for i, row in nodes.iterrows():
          node = etree.SubElement(
              root, 'node', attrib=row[node_attrs].dropna().to_dict())
          for tag in node_tags:
              etree.SubElement(
                  node, 'tag', attrib={'k': tag, 'v': row[tag]})
  
      # append edges to the XML tree
      for i, row in edges.iterrows():
          edge = etree.SubElement(
              root, 'way', attrib=row[edge_attrs].dropna().to_dict())
          etree.SubElement(edge, 'nd', attrib={'ref': row['u']})
          etree.SubElement(edge, 'nd', attrib={'ref': row['v']})
          for tag in edge_tags:
              etree.SubElement(
                  edge, 'tag', attrib={'k': tag, 'v': row[tag]})
  
      et = etree.ElementTree(root)
  
      if not os.path.exists(folder):
          os.makedirs(folder)
  
      et.write(os.path.join(folder, filename))
  
      log('Saved graph "{}" to disk as OSM at "{}" in {:,.2f} seconds'.format(
          G_save.name, os.path.join(folder, filename), time.time() - start_time))