001/* *********************************************************************** *
002 * project: org.matsim.*
003 *                                                                         *
004 * *********************************************************************** *
005 *                                                                         *
006 * copyright       : (C) 2017 by the members listed in the COPYING,        *
007 *                   LICENSE and WARRANTY file.                            *
008 * email           : info at matsim dot org                                *
009 *                                                                         *
010 * *********************************************************************** *
011 *                                                                         *
012 *   This program is free software; you can redistribute it and/or modify  *
013 *   it under the terms of the GNU General Public License as published by  *
014 *   the Free Software Foundation; either version 2 of the License, or     *
015 *   (at your option) any later version.                                   *
016 *   See also COPYING, LICENSE and WARRANTY file                           *
017 *                                                                         *
018 * *********************************************************************** */
019
020/**
021 *
022 */
023package org.matsim.contrib.drt.analysis;
024
025import java.io.BufferedWriter;
026import java.io.FileOutputStream;
027import java.io.IOException;
028import java.util.ArrayList;
029import java.util.HashMap;
030import java.util.List;
031import java.util.Map;
032import java.util.Map.Entry;
033
034import org.apache.commons.lang3.tuple.Pair;
035import org.jfree.chart.ChartUtils;
036import org.jfree.chart.JFreeChart;
037import org.jfree.data.xy.XYSeries;
038import org.matsim.api.core.v01.Coord;
039import org.matsim.api.core.v01.Id;
040import org.matsim.api.core.v01.events.PersonEntersVehicleEvent;
041import org.matsim.api.core.v01.events.handler.PersonEntersVehicleEventHandler;
042import org.matsim.api.core.v01.network.Network;
043import org.matsim.api.core.v01.population.Person;
044import org.matsim.contrib.drt.passenger.events.DrtRequestSubmittedEvent;
045import org.matsim.contrib.drt.passenger.events.DrtRequestSubmittedEventHandler;
046import org.matsim.contrib.drt.run.DrtConfigGroup;
047import org.matsim.contrib.dvrp.optimizer.Request;
048import org.matsim.contrib.dvrp.passenger.PassengerRequestRejectedEvent;
049import org.matsim.contrib.dvrp.passenger.PassengerRequestRejectedEventHandler;
050import org.matsim.contrib.dvrp.passenger.PassengerRequestScheduledEvent;
051import org.matsim.contrib.dvrp.passenger.PassengerRequestScheduledEventHandler;
052import org.matsim.core.api.experimental.events.EventsManager;
053import org.matsim.core.utils.collections.Tuple;
054import org.matsim.core.utils.io.IOUtils;
055
056/**
057 * @author jbischoff
058 */
059public class DrtRequestAnalyzer implements PassengerRequestRejectedEventHandler, PassengerRequestScheduledEventHandler,
060                DrtRequestSubmittedEventHandler, PersonEntersVehicleEventHandler {
061
062        private final Map<Id<Request>, DrtRequestSubmittedEvent> submittedRequests = new HashMap<>();
063        private final Map<Id<Request>, Tuple<Double, Double>> waitTimeCompare = new HashMap<>();
064        private final Map<Id<Person>, PassengerRequestScheduledEvent> scheduledRequests = new HashMap<>();
065        private final List<String> rejections = new ArrayList<>();
066        private final Network network;
067        private final DrtConfigGroup drtCfg;
068
069        public DrtRequestAnalyzer(EventsManager events, Network network, DrtConfigGroup drtCfg) {
070                events.addHandler(this);
071                this.network = network;
072                this.drtCfg = drtCfg;
073        }
074
075        @Override
076        public void reset(int iteration) {
077                submittedRequests.clear();
078                scheduledRequests.clear();
079                waitTimeCompare.clear();
080                rejections.clear();
081        }
082
083        @Override
084        public void handleEvent(PersonEntersVehicleEvent event) {
085                if (this.scheduledRequests.containsKey(event.getPersonId())) {
086                        PassengerRequestScheduledEvent scheduled = scheduledRequests.remove(event.getPersonId());
087                        DrtRequestSubmittedEvent submission = this.submittedRequests.remove(scheduled.getRequestId());
088                        double actualWaitTime = event.getTime() - submission.getTime();
089                        double estimatedWaitTime = scheduled.getPickupTime() - submission.getTime();
090                        waitTimeCompare.put(submission.getRequestId(), Tuple.of(actualWaitTime, estimatedWaitTime));
091
092                }
093        }
094
095        @Override
096        public void handleEvent(PassengerRequestScheduledEvent event) {
097                if (!event.getMode().equals(drtCfg.getMode())) {
098                        return;
099                }
100                DrtRequestSubmittedEvent submission = this.submittedRequests.get(event.getRequestId());
101                if (submission != null) {
102                        this.scheduledRequests.put(submission.getPersonId(), event);
103                } else
104                        throw new RuntimeException("Vehicle allocation without submission?");
105        }
106
107        @Override
108        public void handleEvent(DrtRequestSubmittedEvent event) {
109                if (!event.getMode().equals(drtCfg.getMode())) {
110                        return;
111                }
112                this.submittedRequests.put(event.getRequestId(), event);
113        }
114
115        @Override
116        public void handleEvent(PassengerRequestRejectedEvent event) {
117                if (!event.getMode().equals(drtCfg.getMode())) {
118                        return;
119                }
120                DrtRequestSubmittedEvent submission = this.submittedRequests.remove(event.getRequestId());
121                Coord fromCoord = network.getLinks().get(submission.getFromLinkId()).getCoord();
122                Coord toCoord = network.getLinks().get(submission.getToLinkId()).getCoord();
123                this.rejections.add(submission.getTime()
124                                + ";"
125                                + submission.getPersonId()
126                                + ";"
127                                + submission.getFromLinkId()
128                                + ";"
129                                + submission.getToLinkId()
130                                + ";"
131                                + fromCoord.getX()
132                                + ";"
133                                + fromCoord.getY()
134                                + ";"
135                                + toCoord.getX()
136                                + ";"
137                                + toCoord.getY());
138        }
139
140        /**
141         * @return the waitTimeCompare
142         */
143        public Map<Id<Request>, Tuple<Double, Double>> getWaitTimeCompare() {
144                return waitTimeCompare;
145        }
146
147        /**
148         * @return the rejections
149         */
150        public List<String> getRejections() {
151                return rejections;
152        }
153
154        public void writeAndPlotWaitTimeEstimateComparison(String plotFileName, String textFileName, boolean createChart) {
155                BufferedWriter bw = IOUtils.getBufferedWriter(textFileName);
156
157                XYSeries times = new XYSeries("waittimes", true, true);
158
159                try {
160                        bw.append("RequestId;actualWaitTime;estimatedWaitTime;deviate");
161                        for (Entry<Id<Request>, Tuple<Double, Double>> e : this.waitTimeCompare.entrySet()) {
162                                bw.newLine();
163                                double first = e.getValue().getFirst();
164                                double second = e.getValue().getSecond();
165                                bw.append(e.getKey().toString() + ";" + first + ";" + second + ";" + (first - second));
166                                times.add(first, second);
167                        }
168                        bw.flush();
169                        bw.close();
170
171                        if (createChart) {
172                                final JFreeChart chart2 = DensityScatterPlots.createPlot("Wait times", "Actual wait time [s]",
173                                                "Initially planned wait time [s]", times, Pair.of(0., drtCfg.getMaxWaitTime()));
174                                //                      xAxis.setLowerBound(0);
175                                //                      yAxis.setLowerBound(0);
176                                ChartUtils.writeChartAsPNG(new FileOutputStream(plotFileName), chart2, 1500, 1500);
177                        }
178                } catch (IOException e) {
179                        throw new RuntimeException(e);
180                }
181        }
182}