docs: def load_graphml(filename, folder=None, node_type=int):
      """
      Load a GraphML file from disk and convert the node/edge attributes to
      correct data types.
  
      Parameters
      ----------
      filename : string
          the name of the graphml file (including file extension)
      folder : string
          the folder containing the file, if None, use default data folder
      node_type : type
          (Python type (default: int)) - Convert node ids to this type
  
      Returns
      -------
      networkx multidigraph
      """
      start_time = time.time()
  
      # read the graph from disk
      if folder is None:
          folder = settings.data_folder
      path = os.path.join(folder, filename)
      G = nx.MultiDiGraph(nx.read_graphml(path, node_type=node_type))
  
      # convert graph crs attribute from saved string to correct dict data type
      G.graph['crs'] = ast.literal_eval(G.graph['crs'])
  
      if 'streets_per_node' in G.graph:
          G.graph['streets_per_node'] = ast.literal_eval(G.graph['streets_per_node'])
  
      # convert numeric node tags from string to numeric data types
      log('Converting node and edge attribute data types')
      for _, data in G.nodes(data=True):
          data['osmid'] = node_type(data['osmid'])
          data['x'] = float(data['x'])
          data['y'] = float(data['y'])
  
      # convert numeric, bool, and list node tags from string to correct data types
      for _, _, data in G.edges(data=True, keys=False):
  
          # first parse oneway to bool and length to float - they should always
          # have only 1 value each
          data['oneway'] = ast.literal_eval(data['oneway'])
          data['length'] = float(data['length'])
  
          # these attributes might have a single value, or a list if edge's
          # topology was simplified
          for attr in ['highway', 'name', 'bridge', 'tunnel', 'lanes', 'ref', 'maxspeed', 'service', 'access', 'area', 'landuse', 'width', 'est_width']:
              # if this edge has this attribute, and it starts with '[' and ends
              # with ']', then it's a list to be parsed
              if attr in data and data[attr][0] == '[' and data[attr][-1] == ']':
                  # try to convert the string list to a list type, else leave as
                  # single-value string (and leave as string if error)
                  try:
                      data[attr] = ast.literal_eval(data[attr])
                  except:
                      pass
  
          # osmid might have a single value or a list
          if 'osmid' in data:
              if data['osmid'][0] == '[' and data['osmid'][-1] == ']':
                  # if it's a list, eval the list then convert each element to node_type
                  data['osmid'] = [node_type(i) for i in ast.literal_eval(data['osmid'])]
              else:
                  # if it's not a list, convert it to the node_type
                  data['osmid'] = node_type(data['osmid'])
  
          # if geometry attribute exists, load the string as well-known text to
          # shapely LineString
          if 'geometry' in data:
              data['geometry'] = wkt.loads(data['geometry'])
  
      # remove node_default and edge_default metadata keys if they exist
      if 'node_default' in G.graph:
          del G.graph['node_default']
      if 'edge_default' in G.graph:
          del G.graph['edge_default']
  
      log('Loaded graph with {:,} nodes and {:,} edges in {:,.2f} seconds from "{}"'.format(len(list(G.nodes())),
                                                                                            len(list(G.edges())),
                                                                                            time.time()-start_time,
                                                                                            path))
      return G