MATSIM
VariableIntervalTimeVariantAttribute.java
Go to the documentation of this file.
1 /* *********************************************************************** *
2  * project: org.matsim.*
3  * *
4  * *********************************************************************** *
5  * *
6  * copyright : (C) 2015 by the members listed in the COPYING, *
7  * LICENSE and WARRANTY file. *
8  * email : info at matsim dot org *
9  * *
10  * *********************************************************************** *
11  * *
12  * This program is free software; you can redistribute it and/or modify *
13  * it under the terms of the GNU General Public License as published by *
14  * the Free Software Foundation; either version 2 of the License, or *
15  * (at your option) any later version. *
16  * See also COPYING, LICENSE and WARRANTY file *
17  * *
18  * *********************************************************************** */
19 
20 package org.matsim.core.network;
21 
22 import java.util.Arrays;
23 import java.util.TreeMap;
24 
26 
27 import com.google.common.base.Preconditions;
28 
29 final class VariableIntervalTimeVariantAttribute
30 implements TimeVariantAttribute
31 {
32  private int aEvents = 1;
33  private double[] aValues;
34  private double[] aTimes;
35 
36 
37  @Override
38  public boolean isRecalcRequired()
39  {
40  return (this.aTimes == null) || (this.aTimes.length != this.aEvents);
41  // The first condition just says if there is no material, we don't need to do anything.
42  // yyyy The second condition is a bit weird. It essentially checks if the cached data structure (aTimes) has
43  // as many entries as it should have (given by aEvents). This does need, however, an honest calling of
44  // incChangeEvents(). Why not just compare aTimes.length to changeEvents.length?
45  // Counterargument might be that we may not have changeEvents.length available when we call isRecalcRequired().
46  // I do think, however, that this does not happen and also cannot happen, because we need the info anyway for recalculation.
47  // If going into this direction, then incChangeEvents(), isRecalcRequired(), recalc(...) might not be necessary
48  // as exposed interface methods any more; in contrast, getValue would need more arguments.
49  // kai, jul'17
50  }
51 
52 
53  @Override
54  public void recalc(TreeMap<Double, NetworkChangeEvent> changeEvents,
55  ChangeValueGetter valueGetter, double baseValue)
56  {
57  this.aTimes = new double[this.aEvents];
58  this.aValues = new double[this.aEvents];
59  this.aTimes[0] = Double.NEGATIVE_INFINITY;
60  this.aValues[0] = baseValue;
61 
62  int numEvent = 0;
63  if (changeEvents != null) {
64  // go through all change events in chronological sequence:
65  for (NetworkChangeEvent event : changeEvents.values()) {
66  ChangeValue value = valueGetter.getChangeValue(event);
67  if (value != null) {
68  switch( value.getType() ) {
69  case ABSOLUTE_IN_SI_UNITS:
70  // here, we just need to replace the value:
71  this.aValues[++numEvent] = value.getValue();
72  this.aTimes[numEvent] = event.getStartTime();
73  break;
74  case FACTOR: {
75  // there, the change event multiplies what we have so far:
76  double currentValue = this.aValues[numEvent];
77  this.aValues[++numEvent] = currentValue * value.getValue();
78  this.aTimes[numEvent] = event.getStartTime();
79  break; }
80  case OFFSET_IN_SI_UNITS: {
81  double currentValue = this.aValues[numEvent];
82  this.aValues[++numEvent] = currentValue + value.getValue();
83  this.aTimes[numEvent] = event.getStartTime();
84  break; }
85  default:
86  throw new RuntimeException( "unknown ChangeType" ) ;
87  }
88  }
89  }
90  }
91 
92  if (numEvent != this.aEvents - 1) {
93  throw new RuntimeException("Expected number of change events (" + (this.aEvents - 1)
94  + ") differs from the number of events found (" + numEvent + ")!");
95  }
96  }
97 
98 
99  @Override
100  public double getValue(final double time)
101  {
102  Preconditions.checkArgument(!Double.isNaN(time), "NaN time is not supported");
103  // after we have put everything into an array by recalc, we just need a binary search:
104  int key = Arrays.binarySearch(this.aTimes, time);
105  key = key >= 0 ? key : -key - 2;
106  return this.aValues[key];
107  }
108 
109 
110  @Override
111  public void incChangeEvents()
112  {
113  aEvents++;
114  }
115 
116 
117  @Override
118  public void clearEvents()
119  {
120  aTimes = null;
121  aValues = null;
122  aEvents = 1;
123  }
124 }