MATSIM
SnapshotGenerator.java
Go to the documentation of this file.
1 /* *********************************************************************** *
2  * project: org.matsim.*
3  * SnapshotGenerator.java
4  * *
5  * *********************************************************************** *
6  * *
7  * copyright : (C) 2007 by the members listed in the COPYING, *
8  * LICENSE and WARRANTY file. *
9  * email : info at matsim dot org *
10  * *
11  * *********************************************************************** *
12  * *
13  * This program is free software; you can redistribute it and/or modify *
14  * it under the terms of the GNU General Public License as published by *
15  * the Free Software Foundation; either version 2 of the License, or *
16  * (at your option) any later version. *
17  * See also COPYING, LICENSE and WARRANTY file *
18  * *
19  * *********************************************************************** */
20 
21 package org.matsim.core.events.algorithms;
22 
23 import org.apache.logging.log4j.LogManager;
24 import org.apache.logging.log4j.Logger;
25 import org.matsim.api.core.v01.Id;
26 import org.matsim.api.core.v01.IdMap;
27 import org.matsim.api.core.v01.events.*;
40 
41 import java.util.ArrayList;
42 import java.util.Collection;
43 import java.util.HashMap;
44 import java.util.List;
45 
48 
49  private final static Logger log = LogManager.getLogger(SnapshotGenerator.class);
50  private final Network network;
51  private int lastSnapshotIndex = -1;
52  private final double snapshotPeriod;
54  private final ArrayList<EventLink> linkList;
55  private final HashMap<Id<Person>, EventAgent> eventAgents;
56  private final List<SnapshotWriter> snapshotWriters = new ArrayList<>();
57  private final double capCorrectionFactor;
58  private final double storageCapFactor;
62 
64 
65  public SnapshotGenerator(final Network network, final double snapshotPeriod, final QSimConfigGroup config) {
66  this.network = network;
67  int initialCapacity = (int) (network.getLinks().size() * 1.1);
68  this.eventLinks = new IdMap<>(Link.class);
69  this.linkList = new ArrayList<>(initialCapacity);
70  this.eventAgents = new HashMap<>(1000, 0.95f);
71  this.snapshotPeriod = snapshotPeriod;
72  this.capCorrectionFactor = config.getFlowCapFactor() / network.getCapacityPeriod();
73  this.storageCapFactor = config.getStorageCapFactor();
74  this.snapshotStyle = config.getSnapshotStyle();
75 
76  if (!Double.isNaN(config.getLinkWidthForVis())) {
77  this.linkWidthCalculator.setLinkWidthForVis(config.getLinkWidthForVis());
78  }
79  if (! Double.isNaN(network.getEffectiveLaneWidth())){
80  this.linkWidthCalculator.setLaneWidth(network.getEffectiveLaneWidth());
81  }
82 
83  reset(-1);
84  }
85 
86  public final void addSnapshotWriter(final SnapshotWriter writer) {
87  this.snapshotWriters.add(writer);
88  }
89 
90  public final boolean removeSnapshotWriter(final SnapshotWriter writer) {
91  return this.snapshotWriters.remove(writer);
92  }
93 
94  @Override
95  public void handleEvent(final PersonDepartureEvent event) {
96  testForSnapshot(event.getTime());
97  this.eventLinks.get(event.getLinkId()).departure(getEventAgent(event.getPersonId(), event.getTime()));
98  }
99 
100  @Override
101  public void handleEvent(final PersonArrivalEvent event) {
102  testForSnapshot(event.getTime());
103  this.eventLinks.get(event.getLinkId()).arrival(getEventAgent(event.getPersonId(), event.getTime()));
104  }
105 
106  @Override
107  public void handleEvent(final LinkEnterEvent event) {
108  testForSnapshot(event.getTime());
109  this.eventLinks.get(event.getLinkId()).enter(getEventAgent(delegate.getDriverOfVehicle(event.getVehicleId()), event.getTime()));
110  }
111 
112  @Override
113  public void handleEvent(final LinkLeaveEvent event) {
114  testForSnapshot(event.getTime());
115  this.eventLinks.get(event.getLinkId()).leave(getEventAgent(delegate.getDriverOfVehicle(event.getVehicleId()), event.getTime()));
116  }
117 
118  @Override
119  public void handleEvent(final VehicleEntersTrafficEvent event) {
120  testForSnapshot(event.getTime());
121  this.eventLinks.get(event.getLinkId()).wait2link(getEventAgent(event.getPersonId(), event.getTime()));
122 
123  delegate.handleEvent(event);
124  }
125 
126  @Override
127  public void handleEvent(final PersonStuckEvent event) {
128  testForSnapshot(event.getTime());
129  if (event.getLinkId() != null) { // link id is optional - agent can be teleporting or whatever.
130  this.eventLinks.get(event.getLinkId()).stuck(getEventAgent(event.getPersonId(), event.getTime()));
131  }
132  }
133 
134  @Override
135  public void reset(final int iteration) {
136  this.eventLinks.clear();
137  for (Link link : this.network.getLinks().values()) {
138  final double effectiveCellSize;
139  effectiveCellSize = this.network.getEffectiveCellSize();
140  this.eventLinks.put(link.getId(), new EventLink(link, this.capCorrectionFactor, effectiveCellSize, this.storageCapFactor));
141  }
142  this.linkList.clear();
143  this.linkList.addAll(eventLinks.values());
144  this.eventAgents.clear();
145  this.lastSnapshotIndex = -1;
146 
147  delegate.reset(iteration);
148  }
149 
150  private EventAgent getEventAgent(final Id<Person> id, double time) {
151  EventAgent agent = this.eventAgents.get(id);
152  if (agent == null) {
153  agent = new EventAgent(id, time);
154  this.eventAgents.put(id, agent);
155  }
156  agent.time = time;
157  return agent;
158  }
159 
160  private void testForSnapshot(final double time) {
161  int snapshotIndex = (int) (time / this.snapshotPeriod);
162  if (this.lastSnapshotIndex == -1) {
163  this.lastSnapshotIndex = snapshotIndex;
164  }
165  while (snapshotIndex > this.lastSnapshotIndex) {
166  this.lastSnapshotIndex++;
167  double snapshotTime = this.lastSnapshotIndex * this.snapshotPeriod;
168  doSnapshot(snapshotTime);
169  }
170  }
171 
172  private void doSnapshot(final double time) {
173 
174  if (!this.snapshotWriters.isEmpty()) {
175  Collection<AgentSnapshotInfo> positions = getVehiclePositions(time);
176  for (SnapshotWriter writer : this.snapshotWriters) {
177  writer.beginSnapshot(time);
178  for (AgentSnapshotInfo position : positions) {
179  writer.addAgent(position);
180  }
181  writer.endSnapshot();
182  }
183  }
184  }
185 
186  private Collection<AgentSnapshotInfo> getVehiclePositions(final double time) {
187  Collection<AgentSnapshotInfo> positions = new ArrayList<>();
188  if (this.snapshotStyle == SnapshotStyle.queue) {
189  for (EventLink link : this.linkList) {
190  link.getVehiclePositionsQueue(positions, time, this.builder);
191  }
192  } else if (this.snapshotStyle == SnapshotStyle.equiDist) {
193  for (EventLink link : this.linkList) {
194  link.getVehiclePositionsEquil(positions, time, this.builder);
195  }
196  } else {
197  // log statement to clarify: why only two snapshot styles. Amit Mar'17
198  log.warn("Cannot generate snapshots offline (e.g., from events) for "+this.snapshotStyle
199  + ". This snapshot style is supported during simulation only.");
200  throw new RuntimeException("The snapshotStyle \"" + this.snapshotStyle + "\" is not supported.");
201  }
202  return positions;
203  }
204 
205  public final void finish() {
206  for (SnapshotWriter writer : this.snapshotWriters) {
207  writer.finish();
208  }
209  }
210 
211  private static class EventLink {
212  private final Link link;
213  private final List<EventAgent> drivingQueue;
214  private final List<EventAgent> parkingQueue;
215  private final List<EventAgent> waitingQueue;
216  private final List<EventAgent> buffer;
217 
218  private final double euklideanDist;
219  private final double freespeedTravelTime;
220  private final double spaceCap;
221  private final double storageCapFactor;
222  private final double inverseTimeCap;
223 
224  private final double effectiveCellSize;
225 
226  private EventLink(final Link link2, final double capCorrectionFactor, final double effectiveCellSize, final double storageCapFactor) {
227  this.link = link2;
228  this.drivingQueue = new ArrayList<>();
229  this.parkingQueue = new ArrayList<>();
230  this.waitingQueue = new ArrayList<>();
231  this.buffer = new ArrayList<>();
232  this.euklideanDist = CoordUtils.calcEuclideanDistance(link2.getFromNode().getCoord(), link2.getToNode().getCoord());
233  this.freespeedTravelTime = Math.ceil(this.link.getLength() / this.link.getFreespeed()) + 1;
234  double timeCap = this.link.getCapacity() * capCorrectionFactor;
235  this.storageCapFactor = storageCapFactor;
236  this.inverseTimeCap = 1.0 / timeCap;
237  this.effectiveCellSize = effectiveCellSize;
238  this.spaceCap = (this.link.getLength() * this.link.getNumberOfLanes()) / this.effectiveCellSize * storageCapFactor;
239  }
240 
241  private void enter(final EventAgent agent) {
242  if (agent.currentLink != null) {
243  agent.currentLink.stuck(agent); // use stuck to remove it from wherever it is
244  }
245  agent.currentLink = this;
246  this.drivingQueue.add(agent);
247  }
248 
249  private void leave(final EventAgent agent) {
250  this.drivingQueue.remove(agent);
251  this.buffer.remove(agent);
252  agent.currentLink = null;
253  }
254 
255  private void arrival(final EventAgent agent) {
256  this.buffer.remove(agent);
257  this.drivingQueue.remove(agent);
258  this.parkingQueue.add(agent);
259  }
260 
261  private void departure(final EventAgent agent) {
262  agent.currentLink = this;
263  this.parkingQueue.remove(agent);
264  this.waitingQueue.add(agent);
265  }
266 
267  private void wait2link(final EventAgent agent) {
268  this.waitingQueue.remove(agent);
269  this.buffer.add(agent);
270  }
271 
272  private void stuck(final EventAgent agent) {
273  // vehicles can be anywhere when they get stuck
274  this.drivingQueue.remove(agent);
275  this.parkingQueue.remove(agent);
276  this.waitingQueue.remove(agent);
277  this.buffer.remove(agent);
278  agent.currentLink = null;
279  }
280 
282  return builder
283  .setPersonId(agent.id)
284  .setLinkId(link.getId())
285  //.setVehicleId(???) this should be set
286  .setLane(agent.lane)
287  .setDistanceOnLink(distanceFromNode)
288  .setFromCoord(link.getFromNode().getCoord())
289  .setToCoord(link.getToNode().getCoord())
290  .setLinkLength(link.getLength())
291  .setColorValue(agent.speed)
292  .setAgentState(agentState)
293  .build();
294  }
295 
304  private void getVehiclePositionsQueue(final Collection<AgentSnapshotInfo> positions, final double time, PositionInfo.LinkBasedBuilder builder) {
305  double queueEnd = this.link.getLength(); // the length of the queue jammed vehicles build at the end of the link
306  double vehLen = Math.min( // the length of a vehicle in visualization
307  this.euklideanDist / this.spaceCap, // all vehicles must have place on the link
308  this.effectiveCellSize / this.storageCapFactor); // a vehicle should not be larger than it's actual size
309 
310  // put all cars in the buffer one after the other
311  for (EventAgent agent : this.buffer) {
312 
313  agent.lane = 1 + (agent.intId % NetworkUtils.getNumberOfLanesAsInt(time, this.link));
314  int cmp = (int) (agent.time + this.freespeedTravelTime + this.inverseTimeCap + 2.0);
315  agent.speed = (time > cmp) ? 0.0 : this.link.getFreespeed(time);
316  var position = createAgentSnapshotInfo(builder, agent, link, queueEnd, AgentSnapshotInfo.AgentState.PERSON_DRIVING_CAR);
317  positions.add(position);
318  queueEnd -= vehLen;
319  }
320 
321  /* place other driving cars according the following rule:
322  * - calculate the time how long the vehicle is on the link already
323  * - calculate the position where the vehicle should be if it could drive with freespeed
324  * - if the position is already within the congestion queue, add it to the queue with slow speed
325  * - if the position is not within the queue, just place the car with free speed at that place
326  */
327  double lastDistance = Integer.MAX_VALUE;
328  for (EventAgent agent : this.drivingQueue) {
329  double travelTime = time - agent.time;
330  double distanceOnLink = (this.freespeedTravelTime == 0.0 ? 0.0 : ((travelTime / this.freespeedTravelTime) * this.euklideanDist));
331  if (distanceOnLink > queueEnd) { // vehicle is already in queue
332  distanceOnLink = queueEnd;
333  queueEnd -= vehLen;
334  }
335  if (distanceOnLink >= lastDistance) {
336  /* we have a queue, so it should not be possible that one vehicles overtakes another.
337  * additionally, if two vehicles entered at the same time, they would be drawn on top of each other.
338  * we don't allow this, so in this case we put one after the other. Theoretically, this could lead to
339  * vehicles placed at negative distance when a lot of vehicles all enter at the same time on an empty
340  * link. not sure what to do about this yet... just setting them to 0 currently.
341  */
342  distanceOnLink = lastDistance - vehLen;
343  if (distanceOnLink < 0) distanceOnLink = 0.0;
344  }
345  int cmp = (int) (agent.time + this.freespeedTravelTime + this.inverseTimeCap + 2.0);
346  agent.speed = (time > cmp) ? 0.0 : this.link.getFreespeed(time);
347  agent.lane = 1 + (agent.intId % NetworkUtils.getNumberOfLanesAsInt(this.link));
348  var position = createAgentSnapshotInfo(builder, agent, link, queueEnd, AgentSnapshotInfo.AgentState.PERSON_DRIVING_CAR);
349  positions.add(position);
350  lastDistance = distanceOnLink;
351  }
352 
353  /* Put the vehicles from the waiting list in positions.
354  * Their actual position doesn't matter, so they are just placed
355  * to the coordinates of the from node */
356  int lane = NetworkUtils.getNumberOfLanesAsInt(this.link) + 1; // place them next to the link
357  for (EventAgent agent : this.waitingQueue) {
358 
359  agent.lane = lane;
360  agent.speed = 0;
361  var position = createAgentSnapshotInfo(builder, agent, link, queueEnd, AgentSnapshotInfo.AgentState.PERSON_AT_ACTIVITY);
362  positions.add(position);
363  }
364 
365  /* put the vehicles from the parking list in positions
366  * their actual position doesn't matter, so they are just placed
367  * to the coordinates of the from node */
368  lane = NetworkUtils.getNumberOfLanesAsInt(this.link) + 2; // place them next to the link
369  for (EventAgent agent : this.parkingQueue) {
370  agent.lane = lane;
371  agent.speed = 0;
372  var position = createAgentSnapshotInfo(builder, agent, link, queueEnd, AgentSnapshotInfo.AgentState.PERSON_AT_ACTIVITY);
373  positions.add(position);
374  }
375  }
376 
385  private void getVehiclePositionsEquil(final Collection<AgentSnapshotInfo> positions, final double time, PositionInfo.LinkBasedBuilder builder) {
386  int bufferSize = this.buffer.size();
387  int drivingQueueSize = this.drivingQueue.size();
388  int waitingQueueSize = this.waitingQueue.size();
389  int parkingQueueSize = this.parkingQueue.size();
390  if (bufferSize + drivingQueueSize + waitingQueueSize + parkingQueueSize > 0) {
391  int cnt = bufferSize + drivingQueueSize;
392  int nLanes = NetworkUtils.getNumberOfLanesAsInt(time, this.link);
393  double linkLength = this.link.getLength();
394  if (cnt > 0) {
395  double cellSize = linkLength / cnt;
396  double distFromFromNode = linkLength - cellSize / 2.0;
397  double freespeed = this.link.getFreespeed(time);
398 
399  // the cars in the buffer
400  for (EventAgent agent : this.buffer) {
401  agent.lane = 1 + agent.intId % nLanes;
402  int cmp = (int) (agent.time + this.freespeedTravelTime + this.inverseTimeCap + 2.0);
403  if (time > cmp) {
404  agent.speed = 0.0;
405  } else {
406  agent.speed = freespeed;
407  }
408 
409  var position = createAgentSnapshotInfo(builder, agent, link, distFromFromNode, AgentSnapshotInfo.AgentState.PERSON_DRIVING_CAR);
410  positions.add(position);
411  distFromFromNode -= cellSize;
412  }
413 
414  // the cars in the drivingQueue
415  for (EventAgent agent : this.drivingQueue) {
416  agent.lane = 1 + agent.intId % nLanes;
417  int cmp = (int) (agent.time + this.freespeedTravelTime + this.inverseTimeCap + 2.0);
418  if (time > cmp) {
419  agent.speed = 0.0;
420  } else {
421  agent.speed = freespeed;
422  }
423 
424  var position = createAgentSnapshotInfo(builder, agent, link, distFromFromNode, AgentSnapshotInfo.AgentState.PERSON_DRIVING_CAR);
425  positions.add(position);
426  distFromFromNode -= cellSize;
427  }
428  }
429 
430  // the cars in the waitingQueue
431  // the actual position doesn't matter, so they're just placed next to the link at the end
432  if (waitingQueueSize > 0) {
433  int lane = nLanes + 2;
434  double cellSize = Math.min(this.effectiveCellSize, linkLength / waitingQueueSize);
435  double distFromFromNode = linkLength - cellSize / 2.0;
436  for (EventAgent agent : this.waitingQueue) {
437  agent.lane = lane;
438  agent.speed = 0.0;
439  var position = createAgentSnapshotInfo(builder, agent, link, distFromFromNode, AgentSnapshotInfo.AgentState.PERSON_AT_ACTIVITY);
440  positions.add(position);
441  distFromFromNode -= cellSize;
442  }
443  }
444 
445  // the cars in the parkingQueue
446  // the actual position doesn't matter, so they're distributed next to the link
447  if (parkingQueueSize > 0) {
448  int lane = nLanes + 4;
449  double cellSize = linkLength / parkingQueueSize;
450  double distFromFromNode = linkLength - cellSize / 2.0;
451  for (EventAgent agent : this.parkingQueue) {
452  agent.lane = lane;
453  agent.speed = 0.0;
454  var position = createAgentSnapshotInfo(builder, agent, link, distFromFromNode, AgentSnapshotInfo.AgentState.PERSON_AT_ACTIVITY);
455  positions.add(position);
456  distFromFromNode -= cellSize;
457  }
458  }
459  }
460  }
461  }
462 
463  private static class EventAgent implements Comparable<EventAgent> {
464  protected final Id<Person> id;
465  protected final int intId;
466  protected double time;
467  protected EventLink currentLink = null;
468  protected double speed = 0.0;
469  protected int lane = 1;
470 
471  EventAgent(final Id<Person> id, final double time) {
472  this.id = id;
473  this.time = time;
474  this.intId = id.hashCode();
475  }
476 
477  @Override
478  public int compareTo(final EventAgent o) {
479  return this.id.compareTo(o.id);
480  }
481 
482  @Override
483  public boolean equals(final Object o) {
484  if (o instanceof EventAgent) {
485  return this.id.equals(((EventAgent) o).id);
486  }
487  return false;
488  }
489 
490  @Override
491  public int hashCode() {
492  return this.id.hashCode();
493  }
494  }
495 
496  @Override
498  delegate.handleEvent(event);
499  }
500 }
static int getNumberOfLanesAsInt(final double time, final Link link)
final HashMap< Id< Person >, EventAgent > eventAgents
static double calcEuclideanDistance(Coord coord, Coord other)
Collection< AgentSnapshotInfo > getVehiclePositions(final double time)
Collection< V > values()
Definition: IdMap.java:204
final void addSnapshotWriter(final SnapshotWriter writer)
EventAgent getEventAgent(final Id< Person > id, double time)
void handleEvent(final PersonDepartureEvent event)
void handleEvent(final VehicleEntersTrafficEvent event)
void handleEvent(VehicleLeavesTrafficEvent event)
void handleEvent(final PersonArrivalEvent event)
Map< Id< Link >, ? extends Link > getLinks()
SnapshotGenerator(final Network network, final double snapshotPeriod, final QSimConfigGroup config)
V put(Id< T > key, V value)
Definition: IdMap.java:141
final SnapshotLinkWidthCalculator linkWidthCalculator
final boolean removeSnapshotWriter(final SnapshotWriter writer)