plan_gen.py 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596
  1. #!/usr/bin/env python3
  2. ''' Python generator for MATSim plans. '''
  3. import sys
  4. import time
  5. import numpy as np
  6. import lxml.etree as etree
  7. # constants
  8. MIN_DEPARTURE_TIME = '08:00:00'
  9. MAX_DEPARTURE_TIME = '09:00:00'
  10. WORK_DURATION = '04:00:00'
  11. def read_nodes(input_network):
  12. ''' returns all network nodes as a list '''
  13. if not input_network:
  14. return None
  15. tree = etree.parse(input_network)
  16. return [node for node in tree.xpath("/network/nodes/node")]
  17. def get_seconds(time_str):
  18. ''' returns seconds from a time string '''
  19. h, m, s = time_str.split(':')
  20. return int(h) * 3600 + int(m) * 60 + int(s)
  21. def rand_time(low, high):
  22. ''' returns a random time between low and high bounds '''
  23. low_s = get_seconds(low)
  24. high_s = get_seconds(high)
  25. delta = np.random.randint(high_s - low_s)
  26. return time.strftime('%H:%M:%S', time.gmtime(low_s + delta))
  27. def rand_node_xy(nodes, clusters):
  28. ''' returns a random node coordinates from a list of nodes '''
  29. used_nodes = nodes
  30. if any(clusters):
  31. cluster = np.random.randint(len(clusters))
  32. used_nodes = clusters[cluster]
  33. node = used_nodes[np.random.randint(len(used_nodes))]
  34. return (node.get('x'), node.get('y'))
  35. def rand_person(nodes, home_clusters, work_clusters):
  36. ''' returns a person as a dictionnary of random parameters '''
  37. home_xy = rand_node_xy(nodes, home_clusters)
  38. work_xy = rand_node_xy(nodes, work_clusters)
  39. home_departure = rand_time(MIN_DEPARTURE_TIME, MAX_DEPARTURE_TIME)
  40. return {'home': home_xy, 'work': work_xy, 'home_departure': home_departure}
  41. def create_child(parent_node, child_name, child_attrs=None):
  42. ''' creates an xml child element and set its attributes '''
  43. child = etree.SubElement(parent_node, child_name)
  44. if child_attrs is None:
  45. return child
  46. for attr, value in child_attrs.items():
  47. child.set(attr, value)
  48. return child
  49. def make_plans(persons):
  50. ''' makes xml tree of plans based on persons list '''
  51. plans = etree.Element('plans')
  52. for n, p in enumerate(persons):
  53. person = create_child(plans, 'person', {'id': str(n+1)})
  54. plan = create_child(person, 'plan')
  55. # plan
  56. create_child(plan, 'act', {'type': 'h', 'x': p['home'][0], 'y': p['home'][1], 'end_time': p['home_departure']})
  57. create_child(plan, 'leg', {'mode': 'car'})
  58. create_child(plan, 'act', {'type': 'w', 'x': p['work'][0], 'y': p['work'][1], 'dur': WORK_DURATION})
  59. create_child(plan, 'leg', {'mode': 'car'})
  60. create_child(plan, 'act', {'type': 'h', 'x': p['home'][0], 'y': p['home'][1]})
  61. return plans
  62. if __name__ == '__main__':
  63. # command line arguments
  64. if len(sys.argv) != 5:
  65. print('usage:', sys.argv[0], '<nb_persons> <input_network> <input_home_clusters> <input_work_clusters>')
  66. print('type "" if you don\'t want to use specific clusters')
  67. sys.exit(-1)
  68. NB_PERSONS = int(sys.argv[1])
  69. INPUT_NETWORK = sys.argv[2]
  70. INPUT_HOME_CLUSTERS = sys.argv[3].split(',')
  71. INPUT_WORK_CLUSTERS = sys.argv[4].split(',')
  72. # get data
  73. NODES = read_nodes(INPUT_NETWORK)
  74. HOME_CLUSTERS = [read_nodes(cluster) for cluster in INPUT_HOME_CLUSTERS]
  75. WORK_CLUSTERS = [read_nodes(cluster) for cluster in INPUT_WORK_CLUSTERS]
  76. # make xml
  77. PERSONS = [rand_person(NODES, HOME_CLUSTERS, WORK_CLUSTERS) for _ in range(NB_PERSONS)]
  78. PLANS = make_plans(PERSONS)
  79. # print XML
  80. print('<?xml version="1.0" ?>')
  81. print('<!DOCTYPE plans SYSTEM "http://www.matsim.org/files/dtd/plans_v4.dtd">')
  82. print(etree.tostring(PLANS, pretty_print=True).decode('utf-8'))