MATSIM
CalcLinkStats.java
Go to the documentation of this file.
1 /* *********************************************************************** *
2  * project: org.matsim.*
3  * CalcLinkStats.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.analysis;
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;
30 import org.matsim.core.utils.io.IOUtils;
32 
33 import jakarta.inject.Inject;
34 import java.io.BufferedReader;
35 import java.io.BufferedWriter;
36 import java.io.IOException;
37 import java.util.Map;
38 
44 public class CalcLinkStats {
45 
46  private final static Logger log = LogManager.getLogger(CalcLinkStats.class);
47 
48  private static class LinkData {
49  public final double[][] volumes;
50  public final double[][] ttimes;
51 
52  public LinkData(final double[][] linksVolumes, final double[][] linksTTimes) {
53  this.volumes = linksVolumes.clone();
54  this.ttimes = linksTTimes.clone();
55  }
56  }
57 
58  private double volScaleFactor = 1.0;
59 
60  private int count = 0;
62  private final int nofHours;
63  private final Network network;
64 
65  private static final int MIN = 0;
66  private static final int MAX = 1;
67  private static final int SUM = 2;
68  private static final int NOF_STATS = 3;
69 
70  @Inject
71  public CalcLinkStats(final Network network) {
72  this.network = network;
73  this.linkData = new IdMap<>(Link.class);
74  this.nofHours = 24;
75  reset();
76  }
77 
84  public CalcLinkStats(final Network network, double vol_scale_factor) {
85  this(network);
86  this.volScaleFactor = vol_scale_factor;
87  }
88 
89  public void addData(final VolumesAnalyzer analyzer, final TravelTime ttimes) {
90  this.count++;
91  // TODO verify ttimes has hourly timeBin-Settings
92 
93  // go through all links
94  for (Id<Link> linkId : this.linkData.keySet()) {
95 
96  // retrieve link from link ID
97  Link link = this.network.getLinks().get(linkId);
98 
99  // get the volumes for the link ID from the analyzier
100  double[] volumes = analyzer.getVolumesPerHourForLink(linkId);
101 
102  // get the destination container for the data from link data (could have gotten this through iterator right away)
103  LinkData data = this.linkData.get(linkId);
104 
105  // prepare the sum variables (for volumes);
106  long sumVolumes = 0; // daily (0-24) sum
107 
108  // go through all hours:
109  for (int hour = 0; hour < this.nofHours; hour++) {
110 
111  // get travel time for hour
112  double ttime = ttimes.getLinkTravelTime(link, hour*3600, null, null);
113 
114  // add for daily sum:
115  sumVolumes += volumes[hour];
116 
117  // the following has something to do with the fact that we are doing this for multiple iterations. So there are variations.
118  // this collects min and max. There is, however, no good control over how many iterations this is collected.
119  if (this.count == 1) {
120  data.volumes[MIN][hour] = volumes[hour];
121  data.volumes[MAX][hour] = volumes[hour];
122  data.ttimes[MIN][hour] = ttime;
123  data.ttimes[MAX][hour] = ttime;
124  } else {
125  if (volumes[hour] < data.volumes[MIN][hour]) data.volumes[MIN][hour] = volumes[hour];
126  if (volumes[hour] > data.volumes[MAX][hour]) data.volumes[MAX][hour] = volumes[hour];
127  if (ttime < data.ttimes[MIN][hour]) data.ttimes[MIN][hour] = ttime;
128  if (ttime > data.ttimes[MAX][hour]) data.ttimes[MAX][hour] = ttime;
129  }
130 
131  // this is the regular summing up for each hour
132  data.volumes[SUM][hour] += volumes[hour];
133  data.ttimes[SUM][hour] += volumes[hour] * ttime;
134  }
135  // dataVolumes[.][nofHours] are daily (0-24) values
136  if (this.count == 1) {
137  data.volumes[MIN][this.nofHours] = sumVolumes;
138  data.volumes[SUM][this.nofHours] = sumVolumes;
139  data.volumes[MAX][this.nofHours] = sumVolumes;
140  } else {
141  if (sumVolumes < data.volumes[MIN][this.nofHours]) data.volumes[MIN][this.nofHours] = sumVolumes;
142  data.volumes[SUM][this.nofHours] += sumVolumes;
143  if (sumVolumes > data.volumes[MAX][this.nofHours]) data.volumes[MAX][this.nofHours] = sumVolumes;
144  }
145  }
146  }
147 
148  public void reset() {
149  this.linkData.clear();
150  this.count = 0;
151  log.info( " resetting `count' to zero. This info is here since we want to check when this" +
152  " is happening during normal simulation runs. kai, jan'11") ;
153 
154  // initialize our data-table
155  for (Link link : this.network.getLinks().values()) {
156  LinkData data = new LinkData(new double[NOF_STATS][this.nofHours + 1], new double[NOF_STATS][this.nofHours]);
157  this.linkData.put(link.getId(), data);
158  }
159 
160  }
161 
162  public void writeFile(final String filename) {
163  try (BufferedWriter out = IOUtils.getBufferedWriter(filename)) {
164 
165  // write header
166  out.write("LINK\tORIG_ID\tFROM\tTO\tLENGTH\tFREESPEED\tCAPACITY");
167  for (int i = 0; i < this.nofHours; i++) {
168  out.write("\tHRS" + i + "-" + (i+1) + "min");
169  out.write("\tHRS" + i + "-" + (i+1) + "avg");
170  out.write("\tHRS" + i + "-" + (i+1) + "max");
171  }
172  out.write("\tHRS0-" + this.nofHours + "min");
173  out.write("\tHRS0-" + this.nofHours + "avg");
174  out.write("\tHRS0-" + this.nofHours + "max");
175  for (int i = 0; i < this.nofHours; i++) {
176  out.write("\tTRAVELTIME" + i + "-" + (i+1) + "min");
177  out.write("\tTRAVELTIME" + i + "-" + (i+1) + "avg");
178  out.write("\tTRAVELTIME" + i + "-" + (i+1) + "max");
179  }
180  out.write("\n");
181 
182  // write data
183  for (Map.Entry<Id<Link>, LinkData> entry : this.linkData.entrySet()) {
184  Id<Link> linkId = entry.getKey();
185  LinkData data = entry.getValue();
186  Link link = this.network.getLinks().get(linkId);
187 
188  out.write(linkId.toString());
189  out.write("\t"); // origId, no longer supported
190  out.write("\t" + link.getFromNode().getId().toString());
191  out.write("\t" + link.getToNode().getId().toString());
192  out.write("\t" + Double.toString(link.getLength()));
193  out.write("\t" + Double.toString(link.getFreespeed()));
194  out.write("\t" + Double.toString(link.getCapacity()));
195 
196  // HRS0-1, HRS1-2, ... HRS23-24
197 // int[] sum = {0, 0, 0};
198  for (int i = 0; i < this.nofHours; i++) {
199  out.write("\t" + Double.toString(data.volumes[MIN][i]));
200 // sum[MIN] = sum[MIN] + data.volumes[MIN][i];
201  out.write("\t" + Double.toString((data.volumes[SUM][i]) / this.count));
202 // sum[SUM] = sum[SUM] + data.volumes[SUM][i];
203  out.write("\t" + Double.toString(data.volumes[MAX][i]));
204 // sum[MAX] = sum[MAX] + data.volumes[MAX][i];
205  }
206 
207  // HRS0-nofHours
208  out.write("\t" + Double.toString(data.volumes[MIN][this.nofHours]));
209  out.write("\t" + Double.toString((data.volumes[SUM][this.nofHours]) / this.count));
210  out.write("\t" + Double.toString(data.volumes[MAX][this.nofHours]));
211 
212  // TRAVELTIME0-1, TRAVELTIME1-2, ... TRAVELTIME23-24
213  for (int i = 0; i < this.nofHours; i++) {
214  String ttimesMin = Double.toString(data.ttimes[MIN][i]);
215  out.write("\t" + ttimesMin);
216  if (data.volumes[SUM][i] == 0) {
217  // nobody traveled along the link in this hour, so we cannot calculate an average
218  // use the value available or the minimum instead (min and max should be the same, =freespeed)
219  double ttsum = data.ttimes[SUM][i];
220  if (ttsum != 0.0) {
221  out.write("\t" + Double.toString(ttsum));
222  } else {
223  out.write("\t" + ttimesMin);
224  }
225  } else {
226  double ttsum = data.ttimes[SUM][i];
227  if (ttsum == 0) {
228  out.write("\t" + ttimesMin);
229  } else {
230  out.write("\t" + Double.toString(ttsum / data.volumes[SUM][i]));
231  }
232  }
233  out.write("\t" + Double.toString(data.ttimes[MAX][i]));
234  }
235  out.write("\n");
236  }
237 
238  } catch (IOException e) {
239  log.error("could not write linkstats", e);
240  }
241  }
242 
243  public void readFile(final String filename) {
244  // start with a clean, empty data structure
245  reset();
246 
247  try (BufferedReader reader = IOUtils.getBufferedReader(filename)) {
248 
249  // read header
250  String header = reader.readLine();
251  if (header == null) {
252  // it seems there is no data in this file...
253  return;
254  }
255  // ignore the header, but if there was a header, set the count to 1
256  this.count = 1;
257 
258  // read lines
259  String line = reader.readLine();
260  while (line != null) {
261  String[] parts = StringUtils.explode(line, '\t');
262  if (parts.length == 154) {
263  Id<Link> linkId = Id.create(parts[0], Link.class);
264  LinkData data = this.linkData.get(linkId);
265  if (data == null) {
266  System.err.println("CalcLinkStats.readFile(); unknown link: " + linkId.toString());
267  } else {
268  int baseTTimes;
269  for (int i = 0; i < this.nofHours; i++) {
270  data.volumes[MIN][i] = Double.parseDouble(parts[7 + i*3]);
271  data.volumes[MIN][i] *= this.volScaleFactor;
272  data.volumes[SUM][i] = Double.parseDouble(parts[8 + i*3]);
273  data.volumes[SUM][i] *= this.volScaleFactor;
274  data.volumes[MAX][i] = Double.parseDouble(parts[9 + i*3]);
275  data.volumes[MAX][i] *= this.volScaleFactor;
276  baseTTimes = 7 + (this.nofHours+1)*3;
277  data.ttimes[MIN][i] = Double.parseDouble(parts[baseTTimes + i*3]);
278  if (data.volumes[SUM][i] == 0) {
279  data.ttimes[SUM][i] = Double.parseDouble(parts[baseTTimes + i*3 + 1]);
280  } else {
281  data.ttimes[SUM][i] = Double.parseDouble(parts[baseTTimes + i*3 + 1]) * data.volumes[SUM][i];
282  }
283  data.ttimes[MAX][i] = Double.parseDouble(parts[baseTTimes + i*3 + 2]);
284  }
285  data.volumes[MIN][this.nofHours] = Double.parseDouble(parts[7 + this.nofHours*3]);
286  data.volumes[MIN][this.nofHours] *= this.volScaleFactor;
287  data.volumes[SUM][this.nofHours] = Double.parseDouble(parts[8 + this.nofHours*3]);
288  data.volumes[SUM][this.nofHours] *= this.volScaleFactor;
289  data.volumes[MAX][this.nofHours] = Double.parseDouble(parts[9 + this.nofHours*3]);
290  data.volumes[MAX][this.nofHours] *= this.volScaleFactor;
291  }
292  }
293  else if (parts.length == 153) {
294  String linkId = parts[0];
295  LinkData data = this.linkData.get(Id.create(linkId, Link.class));
296  if (data == null) {
297  System.err.println("CalcLinkStats.readFile(); unknown link: " + linkId);
298  } else {
299  int baseTTimes;
300  for (int i = 0; i < this.nofHours; i++) {
301  data.volumes[MIN][i] = Double.parseDouble(parts[6 + i*3]);
302  data.volumes[MIN][i] *= this.volScaleFactor;
303  data.volumes[SUM][i] = Integer.parseInt(parts[7 + i*3]);
304  data.volumes[SUM][i] *= this.volScaleFactor;
305  data.volumes[MAX][i] = Double.parseDouble(parts[8 + i*3]);
306  data.volumes[MAX][i] *= this.volScaleFactor;
307  baseTTimes = 6 + (this.nofHours+1)*3;
308  data.ttimes[MIN][i] = Double.parseDouble(parts[baseTTimes + i*3]);
309  if (data.volumes[SUM][i] == 0) {
310  data.ttimes[SUM][i] = Double.parseDouble(parts[baseTTimes + i*3 + 1]);
311  } else {
312  data.ttimes[SUM][i] = Double.parseDouble(parts[baseTTimes + i*3 + 1]) * data.volumes[SUM][i];
313  }
314  data.ttimes[MAX][i] = Double.parseDouble(parts[baseTTimes + i*3 + 2]);
315  }
316  data.volumes[MIN][this.nofHours] = Double.parseDouble(parts[6 + this.nofHours*3]);
317  data.volumes[MIN][this.nofHours] *= this.volScaleFactor;
318  data.volumes[SUM][this.nofHours] = Double.parseDouble(parts[7 + this.nofHours*3]);
319  data.volumes[SUM][this.nofHours] *= this.volScaleFactor;
320  data.volumes[MAX][this.nofHours] = Double.parseDouble(parts[8 + this.nofHours*3]);
321  data.volumes[MAX][this.nofHours] *= this.volScaleFactor;
322  }
323  }
324  else {
325  System.err.println("CalcLinkStats.readFile(); line cannot be parsed: " + line + " number of colums is: " + parts.length);
326  break;
327  }
328  line = reader.readLine();
329  }
330 
331  } catch (IOException e) {
332  log.error("could not read linkstats.", e);
333  }
334  }
335 
340  public double[] getAvgLinkVolumes(final Id<Link> linkId) {
341  LinkData data = this.linkData.get(linkId);
342  if (data == null) {
343  return new double[0];
344  }
345  if (this.count == 0) {
346  return new double[0];
347  }
348  double[] volumes = new double[this.nofHours];
349  for (int i = 0; i < this.nofHours; i++) {
350  volumes[i] = (data.volumes[SUM][i]) / (this.count);
351  }
352  return volumes;
353  }
354 
362  @Deprecated
363  protected double[] getAvgTravelTimes(final Id<Link> linkId) {
364  LinkData data = this.linkData.get(linkId);
365  if (data == null) {
366  return new double[0];
367  }
368  if (this.count == 0) {
369  return new double[0];
370  }
371  double[] ttimesMin = new double[this.nofHours];
372  double[] ttimesSum = new double[this.nofHours];
373  double[] volumes = new double[this.nofHours];
374 
375  double[] avgTTimes = new double[this.nofHours];
376 
377  for (int i = 0; i < this.nofHours; i++) {
378  volumes[i] = (data.volumes[SUM][i]) / (this.count);
379  ttimesMin[i] = (data.ttimes[MIN][i]) / (this.count);
380  ttimesSum[i] = (data.ttimes[SUM][i]) / (this.count);
381 
382  if (volumes[i] == 0.) {
383  avgTTimes[i] = ttimesMin[i];
384  } else {
385  avgTTimes[i] = ttimesSum[i] / volumes[i];
386  }
387  }
388  return avgTTimes;
389  }
390 
391 }
double [] getVolumesPerHourForLink(final Id< Link > linkId)
Set< Id< T > > keySet()
Definition: IdMap.java:189
static BufferedReader getBufferedReader(URL url, Charset charset)
Definition: IOUtils.java:321
static String [] explode(final String str, final char delimiter, final int limit)
double getLinkTravelTime(Link link, double time, Person person, Vehicle vehicle)
static BufferedWriter getBufferedWriter(URL url, Charset charset, boolean append)
Definition: IOUtils.java:390
static< T > Id< T > create(final long key, final Class< T > type)
Definition: Id.java:68
Map< Id< Link >, ? extends Link > getLinks()
V put(Id< T > key, V value)
Definition: IdMap.java:141