001/* *********************************************************************** *
002 * project: org.matsim.*
003 * CalcLegTimes.java
004 *                                                                         *
005 * *********************************************************************** *
006 *                                                                         *
007 * copyright       : (C) 2007 by the members listed in the COPYING,        *
008 *                   LICENSE and WARRANTY file.                            *
009 * email           : info at matsim dot org                                *
010 *                                                                         *
011 * *********************************************************************** *
012 *                                                                         *
013 *   This program is free software; you can redistribute it and/or modify  *
014 *   it under the terms of the GNU General Public License as published by  *
015 *   the Free Software Foundation; either version 2 of the License, or     *
016 *   (at your option) any later version.                                   *
017 *   See also COPYING, LICENSE and WARRANTY file                           *
018 *                                                                         *
019 * *********************************************************************** */
020
021package org.matsim.analysis;
022
023import org.apache.log4j.Logger;
024import org.matsim.api.core.v01.IdMap;
025import org.matsim.api.core.v01.events.ActivityEndEvent;
026import org.matsim.api.core.v01.events.ActivityStartEvent;
027import org.matsim.api.core.v01.events.PersonArrivalEvent;
028import org.matsim.api.core.v01.events.PersonDepartureEvent;
029import org.matsim.api.core.v01.events.handler.ActivityEndEventHandler;
030import org.matsim.api.core.v01.events.handler.ActivityStartEventHandler;
031import org.matsim.api.core.v01.events.handler.PersonArrivalEventHandler;
032import org.matsim.api.core.v01.events.handler.PersonDepartureEventHandler;
033import org.matsim.api.core.v01.population.Person;
034import org.matsim.core.api.experimental.events.EventsManager;
035import org.matsim.core.utils.io.IOUtils;
036import org.matsim.core.utils.io.UncheckedIOException;
037import org.matsim.core.utils.misc.Time;
038
039import javax.inject.Inject;
040import java.io.BufferedWriter;
041import java.io.IOException;
042import java.util.Map;
043import java.util.TreeMap;
044
045/**
046 * @author mrieser
047 *
048 * Calculates the distribution of legs-durations, e.g. how many legs took at
049 * most 5 minutes, how many between 5 and 10 minutes, and so on.
050 * Also calculates the average trip duration.
051 * Trips ended because of vehicles being stuck are not counted.
052 */
053public class CalcLegTimes implements PersonDepartureEventHandler, PersonArrivalEventHandler, 
054        ActivityEndEventHandler, ActivityStartEventHandler {
055
056        private final static Logger log = Logger.getLogger(CalcLegTimes.class);
057        
058        private static final int SLOT_SIZE = 300;       // 5-min slots
059        private static final int MAXINDEX = 12; // slots 0..11 are regular slots, slot 12 is anything above
060
061        private final IdMap<Person, Double> agentDepartures = new IdMap<>(Person.class);
062        private final IdMap<Person, Double> agentArrivals = new IdMap<>(Person.class);
063        private final Map<String, int[]> legStats = new TreeMap<>();
064        private final IdMap<Person, String> previousActivityTypes = new IdMap<>(Person.class);
065        private double sumTripDurations = 0;
066        private int sumTrips = 0;
067
068        @Inject
069        CalcLegTimes(EventsManager eventsManager) {
070                eventsManager.addHandler(this);
071        }
072
073        public CalcLegTimes() {
074
075        }
076
077        @Override
078        public void handleEvent(ActivityEndEvent event) {
079                this.previousActivityTypes.put(event.getPersonId(), event.getActType());
080        }
081        
082        @Override
083        public void handleEvent(final PersonDepartureEvent event) {
084                this.agentDepartures.put(event.getPersonId(), event.getTime());
085        }
086
087        @Override
088        public void handleEvent(final PersonArrivalEvent event) {
089                this.agentArrivals.put(event.getPersonId(), event.getTime());
090        }
091
092
093        @Override
094        public void handleEvent(ActivityStartEvent event) {
095                Double depTime = this.agentDepartures.remove(event.getPersonId());
096                Double arrTime = this.agentArrivals.remove(event.getPersonId());
097                if (depTime != null) {
098                        double travTime = arrTime - depTime;
099                        String fromActType = previousActivityTypes.remove(event.getPersonId());
100                        String toActType = event.getActType();
101                        String legType = fromActType + "---" + toActType;
102                        int[] stats = this.legStats.get(legType);
103                        if (stats == null) {
104                                stats = new int[MAXINDEX+1];
105                                for (int i = 0; i <= MAXINDEX; i++) {
106                                        stats[i] = 0;
107                                }
108                                this.legStats.put(legType, stats);
109                        }
110                        stats[getTimeslotIndex(travTime)]++;
111
112                        this.sumTripDurations += travTime;
113                        this.sumTrips++;
114                }
115        }
116
117        
118        @Override
119        public void reset(final int iteration) {
120                this.previousActivityTypes.clear();
121                this.agentDepartures.clear();
122                this.legStats.clear();
123                this.sumTripDurations = 0;
124                this.sumTrips = 0;
125        }
126
127        public Map<String, int[]> getLegStats() {
128                return this.legStats;
129        }
130
131        public static int getTimeslotIndex(final double time_s) {
132                int idx = (int)(time_s / SLOT_SIZE);
133                if (idx > MAXINDEX) idx = MAXINDEX;
134                return idx;
135        }
136
137        public double getAverageTripDuration() {
138                return (this.sumTripDurations / this.sumTrips);
139        }
140
141        public void writeStats(final String filename) {
142                try (BufferedWriter legStatsFile = IOUtils.getBufferedWriter(filename)) {
143                        writeStats(legStatsFile);
144                } catch (IOException e) {
145                        log.error(e);
146                }
147        }
148
149        public void writeStats(final java.io.Writer out) throws UncheckedIOException {
150                try {
151                boolean first = true;
152                for (Map.Entry<String, int[]> entry : this.legStats.entrySet()) {
153                        String key = entry.getKey();
154                        int[] counts = entry.getValue();
155                        if (first) {
156                                first = false;
157                                out.write("pattern");
158                                for (int i = 0; i < counts.length; i++) {
159                                        out.write("\t" + (i*SLOT_SIZE/60) + "+");
160                                }
161                                out.write("\n");
162                        }
163                        out.write(key);
164            for (int count : counts) {
165                out.write("\t" + count);
166            }
167                        out.write("\n");
168                }
169                out.write("\n");
170                if (this.sumTrips == 0) {
171                        out.write("average trip duration: no trips!");
172                } else {
173                        out.write("average trip duration: "
174                                        + (this.sumTripDurations / this.sumTrips) + " seconds = "
175                                        + Time.writeTime(((int)(this.sumTripDurations / this.sumTrips))));
176                }
177                out.write("\n");
178                } catch (IOException e) {
179                        throw new UncheckedIOException(e);
180                } finally {
181                        try {
182                                out.flush();
183                        } catch (IOException e) {
184                                log.error(e);
185                        }
186                }
187        }
188}