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

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))