plan_gen.py 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163
  1. #!/usr/bin/env python3
  2. ''' plan_gen main functions '''
  3. import time
  4. import numpy as np
  5. import lxml.etree as etree
  6. # constants
  7. # ------------------
  8. MIN_DEPARTURE_TIME = '08:00:00'
  9. MAX_DEPARTURE_TIME = '09:00:00'
  10. WORK_DURATION = '04:00:00'
  11. # utils
  12. # ------------------
  13. def parse_value(string):
  14. ''' convert string to int, float or string '''
  15. try:
  16. return int(string)
  17. except ValueError:
  18. try:
  19. return float(string)
  20. except ValueError:
  21. return string
  22. def parse_params(param_str):
  23. ''' parse a param string to a dict '''
  24. dict_params = {}
  25. if param_str:
  26. for key_value_str in param_str.split(','):
  27. key, value = key_value_str.split('=')
  28. if key in ['hc', 'wc']:
  29. coords = value.split('|')
  30. dict_params[key] = [np.fromstring(str(x), dtype=int, sep=':') for x in coords]
  31. elif key in ['hr', 'wr']:
  32. dict_params[key] = np.fromstring(str(value), dtype=int, sep='|')
  33. else:
  34. dict_params[key] = parse_value(value)
  35. return dict_params
  36. def get_seconds(time_str):
  37. ''' returns seconds from a time string '''
  38. h, m, s = time_str.split(':')
  39. return int(h) * 3600 + int(m) * 60 + int(s)
  40. def make_gaussian(size, center=None, radius=10):
  41. ''' make a square gaussian kernel '''
  42. x = np.arange(0, size, 1, float)
  43. y = x[:, np.newaxis]
  44. if center is None:
  45. x0 = y0 = size // 2
  46. else:
  47. x0 = center[0]
  48. y0 = center[1]
  49. return np.exp(-4*np.log(2) * ((x-x0)**2 + (y-y0)**2) / radius**2)
  50. # main functions
  51. # ------------------
  52. def make_clusters(nb_clusters, nodes):
  53. ''' make a grid of (nb_clusters*nb_clusters) from a nodes list '''
  54. xmin, xmax, ymin, ymax = get_extrem_nodes(nodes)
  55. dx = (xmax - xmin) / nb_clusters
  56. dy = (ymax - ymin) / nb_clusters
  57. clusters = np.empty((nb_clusters, nb_clusters), dtype=object)
  58. for node in nodes:
  59. x, y = (float(node.get('x')) - xmin, float(node.get('y')) - ymin)
  60. i, j = (int(x/dx), int(y/dy))
  61. if i >= nb_clusters:
  62. i -= 1
  63. if j >= nb_clusters:
  64. j -= 1
  65. if clusters[i][j] is None:
  66. clusters[i][j] = []
  67. clusters[i][j] += [node]
  68. return clusters
  69. def make_densities(nb_clusters, centers=None, radius=None):
  70. ''' make a list of gaussian probability densities '''
  71. if centers is None:
  72. return make_gaussian(nb_clusters, radius=nb_clusters/2)
  73. densities = np.zeros((nb_clusters, nb_clusters))
  74. for n, c in enumerate(centers):
  75. densities += make_gaussian(nb_clusters, center=c, radius=radius[n])
  76. return densities
  77. def clean_densities(densities, clusters):
  78. ''' clean density probability if a cluster is empty '''
  79. densities = densities.flatten()
  80. clusters = clusters.flatten()
  81. for n, c in enumerate(clusters):
  82. if c is None:
  83. densities[n] = 0.0
  84. return densities
  85. # random generators
  86. # ------------------
  87. def rand_time(low, high):
  88. ''' returns a random time between low and high bounds '''
  89. low_s = get_seconds(low)
  90. high_s = get_seconds(high)
  91. delta = np.random.randint(high_s - low_s)
  92. return time.strftime('%H:%M:%S', time.gmtime(low_s + delta))
  93. def rand_node_xy(nodes, clusters, densities):
  94. ''' returns a random node coordinates from a random cluster '''
  95. node = None
  96. clusters = clusters.flatten()
  97. densities = densities.flatten()
  98. cluster = np.random.choice(clusters, p=densities/sum(densities))
  99. node = cluster[np.random.randint(len(cluster))]
  100. return (node.get('x'), node.get('y'))
  101. def rand_person(nodes, clusters, h_dens, w_dens):
  102. ''' returns a person as a dictionnary of random parameters '''
  103. home_xy = rand_node_xy(nodes, clusters, h_dens)
  104. work_xy = rand_node_xy(nodes, clusters, w_dens)
  105. home_departure = rand_time(MIN_DEPARTURE_TIME, MAX_DEPARTURE_TIME)
  106. return {'home': home_xy, 'work': work_xy, 'home_departure': home_departure}
  107. # xml builders
  108. # ------------------
  109. def make_child(parent_node, child_name, child_attrs=None):
  110. ''' creates an xml child element and set its attributes '''
  111. child = etree.SubElement(parent_node, child_name)
  112. if child_attrs is None:
  113. return child
  114. for attr, value in child_attrs.items():
  115. child.set(attr, value)
  116. return child
  117. def make_plans(persons):
  118. ''' makes xml tree of plans based on persons list '''
  119. plans = etree.Element('plans')
  120. for n, p in enumerate(persons):
  121. person = make_child(plans, 'person', {'id': str(n+1)})
  122. plan = make_child(person, 'plan')
  123. # plan
  124. make_child(plan, 'act', {'type': 'h', 'x': p['home'][0], 'y': p['home'][1], 'end_time': p['home_departure']})
  125. make_child(plan, 'leg', {'mode': 'car'})
  126. make_child(plan, 'act', {'type': 'w', 'x': p['work'][0], 'y': p['work'][1], 'dur': WORK_DURATION})
  127. make_child(plan, 'leg', {'mode': 'car'})
  128. make_child(plan, 'act', {'type': 'h', 'x': p['home'][0], 'y': p['home'][1]})
  129. return plans
  130. # xml readers
  131. # ------------------
  132. def get_nodes(input_network):
  133. ''' returns all network nodes as a list '''
  134. if not input_network:
  135. return None
  136. tree = etree.parse(input_network)
  137. return [node for node in tree.xpath("/network/nodes/node")]
  138. def get_extrem_nodes(nodes):
  139. ''' returns extremum coordinates of a nodeslist '''
  140. x = [float(node.get('x')) for node in nodes]
  141. y = [float(node.get('y')) for node in nodes]
  142. return min(x), max(x), min(y), max(y)