1# Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.org/sumo
2# Copyright (C) 2016-2019 German Aerospace Center (DLR) and others.
3# SUMOPy module
4# Copyright (C) 2012-2017 University of Bologna - DICAM
5# This program and the accompanying materials
6# are made available under the terms of the Eclipse Public License v2.0
7# which accompanies this distribution, and is available at
8# http://www.eclipse.org/legal/epl-v20.html
9# SPDX-License-Identifier: EPL-2.0
10
11# @file    demandbase.py
12# @author  Joerg Schweizer
13# @date
14# @version $Id$
15
16
17import numpy as np
18import agilepy.lib_base.classman as cm
19import agilepy.lib_base.arrayman as am
20import agilepy.lib_base.xmlman as xm
21from agilepy.lib_base.misc import random_choice, get_inversemap
22
23OPTIONMAP_POS_DEPARTURE = {"random": -1, "free": -2,
24                           "random_free": -3, "base": -4, "last": -5, "first": -6}
25OPTIONMAP_POS_ARRIVAL = {"random": -1, "max": -2}
26OPTIONMAP_SPEED_DEPARTURE = {"random": -1, "max": -2}
27OPTIONMAP_SPEED_ARRIVAL = {"current": -1}
28OPTIONMAP_LANE_DEPART = {"random": -1, "free": 2,
29                         "allowed": -3, "best": -4, "first": -5}
30OPTIONMAP_LANE_ARRIVAL = {"current": -1}
31
32
33class ModeShares(am.ArrayObjman):
34    """
35    Utility table with some default mode shares.
36    """
37
38    def __init__(self, ident, parent, modes, **kwargs):
39
40        self._init_objman(ident, parent=parent, name='Mode shares',
41                          version=0.0,
42                          **kwargs)
43
44        self.add_col(am.IdsArrayConf('ids_mode', modes,
45                                     groupnames=['parameters'],
46                                     name='Mode ID',
47                                     info='Transport Mode ID.',
48                                     ))
49
50        self.add_col(am.ArrayConf('shares', '',
51                                  dtype=np.float32,
52                                  is_index=True,
53                                  groupnames=['parameters'],
54                                  perm='rw',
55                                  name='Share',
56                                  info='Mode share.',
57                                  ))
58
59        # self.add_col(am.ArrayConf( 'speeds_max', 50.0/3.6,
60        #                            dtype = np.float32,
61        #                            groupnames = ['parameters'],
62        #                            perm='rw',
63        #                            name = 'Max. Speed',
64        #                            unit = 'm/s',
65        #                            info = 'Maximum possible speed for this mode. Speed is used to estimate free flow link travel times, mainly for routig purposes. Note that speeds are usully limited by the lane speed attribute',
66        #                            ))
67        self._init_attributes()
68        self.add_default()
69
70    def _init_attributes(self, landuse=None):
71        # self.add_col(SumoIdsConf('Activitytypes'))
72        pass
73
74    def add_share(self, mode, share):
75        modes = self.ids_mode.get_linktab()
76        return self.add_row(ids_mode=modes.get_id_from_formatted(mode),
77                            shares=share)
78
79    def add_default(self):
80        """
81        Sets the default maximum possible speed for certain modes.
82        """
83        self.add_share("pedestrian", 0.1)
84        self.add_share("bicycle", 0.1)
85        self.add_share("motorcycle", 0.1)
86        self.add_share("passenger", 0.5)
87        self.add_share("bus", 0.2)
88
89    def get_modes_random(self, n):
90        """
91        Return a vector with mode IDs of length n.
92        """
93        ids = self.get_ids()
94        ids_modes_all = self.ids_mode[ids]
95        return ids_modes_all[random_choice(n, self.shares[ids])]
96
97
98class ActivityTypes(am.ArrayObjman):
99    # http://www.sumo.dlr.de/userdoc/Networks/Building_Networks_from_own_XML-descriptions.html#Edge_Descriptions
100    def __init__(self, ident, demand, **kwargs):
101
102        self._init_objman(ident, parent=demand, name='Activity Types',
103                          version=0.0,
104                          xmltag=('actTypes', 'actType', 'names'),
105                          **kwargs)
106
107        self._init_attributes()
108        self.add_default()
109
110    def _init_attributes(self, landuse=None):
111        # self.add_col(SumoIdsConf('Activitytypes'))
112
113        self.add_col(am.ArrayConf('names', '',
114                                  dtype=np.object,
115                                  is_index=True,
116                                  groupnames=['parameters'],
117                                  perm='rw',
118                                  name='Type name',
119                                  info='Human readable name of activity type.',
120                                  ))
121
122        self.add_col(am.ArrayConf('symbols', '',
123                                  dtype=np.object,
124                                  perm='rw',
125                                  is_index=True,
126                                  name='Type symbol',
127                                  info='Symbol of activity type name. Used to represent activity sequences.',
128                                  ))
129
130        self.add_col(am.ArrayConf('descriptions', '',
131                                  dtype=np.object,
132                                  perm='rw',
133                                  name='Description',
134                                  info='Description of activity.',
135                                  ))
136
137        # this works only for first init
138        # if landuse is not None:
139        self.add_col(am.IdlistsArrayConf('ids_landusetypes', self.parent.get_scenario().landuse.landusetypes,
140                                         name='Landuse types',
141                                         info="Landuse type IDs, eher this activity type can take place.",
142                                         ))
143
144        self.add_col(am.ArrayConf('hours_begin_earliest', 0.0,
145                                  dtype=np.float32,
146                                  groupnames=['parameters'],
147                                  perm='rw',
148                                  name='Earliest hour begin',
149                                  unit='h',
150                                  info='Default value for earliest hour when this activity can begin.',
151                                  ))
152
153        self.add_col(am.ArrayConf('hours_begin_latest', 1.0,
154                                  dtype=np.float32,
155                                  groupnames=['parameters'],
156                                  perm='rw',
157                                  name='Latest begin hour',
158                                  unit='h',
159                                  info='Default value for latest hour when this activity can begin.',
160                                  ))
161
162        self.add_col(am.ArrayConf('durations_min', 6.0,
163                                  dtype=np.float32,
164                                  groupnames=['parameters'],
165                                  perm='rw',
166                                  name='Min. Duration',
167                                  unit='h',
168                                  info='Default value for minimum activity duration for a person within a day.',
169                                  ))
170
171        self.add_col(am.ArrayConf('durations_max', 8.0,
172                                  dtype=np.float32,
173                                  groupnames=['parameters'],
174                                  perm='rw',
175                                  name='Max. Duration',
176                                  unit='h',
177                                  info='Default value for maximum activity duration for a person within a day.',
178                                  ))
179
180    def format_ids(self, ids):
181        return ', '.join(self.names[ids])
182
183    def get_id_from_formatted(self, idstr):
184        return self.names.get_id_from_index(idstr)
185
186    def get_ids_from_formatted(self, idstrs):
187        return self.names.get_ids_from_indices_save(idstrs.split(','))
188
189    def get_id_from_name(self, activitytypename):
190        return self.names.get_id_from_index(activitytypename)
191
192    def get_id_from_symbol(self, activitytypesymbol):
193        return self.symbols.get_id_from_index(activitytypesymbol)
194
195    def add_default(self):
196        """
197        Sets the default maximum possible speed for certain modes.
198        """
199        landusetypekeys = self.parent.get_scenario().landuse.landusetypes.typekeys
200        self.add_row(names='none',
201                     descriptions='None activity type. Will be skipped when planning.',
202                     ids_landusetypes=landusetypekeys.get_ids_from_indices([]),
203                     symbols='n',
204                     hours_begin_earliest=0.0,
205                     hours_begin_latest=0.0,
206                     durations_min=0.0,
207                     durations_max=0.0,
208                     )
209
210        self.add_row(names='home',
211                     descriptions='General home activity, like sleeping, eating, watching TV, etc.',
212                     ids_landusetypes=landusetypekeys.get_ids_from_indices(
213                         ['residential', 'mixed']),
214                     symbols='h',
215                     hours_begin_earliest=-1.0,
216                     hours_begin_latest=-1.0,
217                     durations_min=7.0,
218                     durations_max=8.0,
219                     )
220
221        self.add_row(names='work',
222                     descriptions="""Work activity, for example work in
223                     industry, offices or as employee at
224                     educational facilities.""",
225                     ids_landusetypes=landusetypekeys.get_ids_from_indices(
226                         ['industrial', 'commercial', 'education', 'mixed']),
227                     symbols='w',
228                     hours_begin_earliest=8.5,
229                     hours_begin_latest=9.0,
230                     durations_min=6.0,
231                     durations_max=9.0,
232                     )
233
234        self.add_row(names='education',
235                     descriptions='Education activity, for example visiting courses at schools or at universities.',
236                     ids_landusetypes=landusetypekeys.get_ids_from_indices(
237                         ['education', ]),
238                     symbols='e',
239                     hours_begin_earliest=8.0,
240                     hours_begin_latest=10.0,
241                     durations_min=4.0,
242                     durations_max=6.0,
243                     )
244
245        self.add_row(names='shopping',
246                     descriptions='Shopping activity',
247                     ids_landusetypes=landusetypekeys.get_ids_from_indices(
248                         ['commercial', 'mixed']),
249                     symbols='s',
250                     hours_begin_earliest=16.0,
251                     hours_begin_latest=19.0,
252                     durations_min=0.2,
253                     durations_max=2.0,
254                     )
255
256        self.add_row(names='leisure',
257                     descriptions='Leisure activity',
258                     ids_landusetypes=landusetypekeys.get_ids_from_indices(
259                         ['leisure', 'mixed']),
260                     symbols='l',
261                     hours_begin_earliest=12.0,
262                     hours_begin_latest=15.0,
263                     durations_min=1.0,
264                     durations_max=3.0,
265                     )
266
267
268class DemandobjMixin:
269    def export_trips_xml(self, filepath=None, encoding='UTF-8',
270                         ids_vtype_exclude=[]):
271        """
272        Export trips to SUMO xml file.
273        Method takes care of sorting trips by departure time.
274        """
275        return False
276
277    def get_writexmlinfo(self, is_route=False):
278        """
279        Returns three array where the first array is the
280        begin time of the first vehicle and the second array is the
281        write function to be called for the respectice vehicle and
282        the third array contains the vehicle ids
283
284        Method used to sort trips when exporting to route or trip xml file
285        """
286        return [], [], []
287
288    def config_results(self, results):
289        # tripresults = res.Tripresults(          'tripresults', results,
290        #                                        self,
291        #                                        self.get_net().edges
292        #                                        )
293        #
294        #
295        #results.add_resultobj(tripresults, groupnames = ['Trip results'])
296        pass
297
298    def process_results(self, results, process=None):
299        pass
300
301    def get_time_depart_first(self):
302        return np.inf
303
304    def get_time_depart_last(self):
305        return 0.0
306