playground.anhorni.surprice.analysis.ModeSharesEventHandler.java Source code

Java tutorial

Introduction

Here is the source code for playground.anhorni.surprice.analysis.ModeSharesEventHandler.java

Source

/* *********************************************************************** *
 * project: org.matsim.*
 *                                                                         *
 * *********************************************************************** *
 *                                                                         *
 * copyright       : (C) 2012 by the members listed in the COPYING,        *
 *                   LICENSE and WARRANTY file.                            *
 * email           : info at matsim dot org                                *
 *                                                                         *
 * *********************************************************************** *
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *   See also COPYING, LICENSE and WARRANTY file                           *
 *                                                                         *
 * *********************************************************************** */
package playground.anhorni.surprice.analysis;

import herbie.running.population.algorithms.AbstractClassifiedFrequencyAnalysis;
import org.apache.commons.math.stat.Frequency;
import org.apache.commons.math.util.ResizableDoubleArray;
import org.apache.log4j.Logger;
import org.jfree.chart.ChartFactory;
import org.jfree.chart.ChartUtilities;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.plot.PlotOrientation;
import org.jfree.data.statistics.HistogramDataset;
import org.jfree.data.statistics.HistogramType;
import org.jfree.data.xy.XYSeries;
import org.matsim.api.core.v01.Id;
import org.matsim.api.core.v01.events.PersonArrivalEvent;
import org.matsim.api.core.v01.events.PersonDepartureEvent;
import org.matsim.api.core.v01.events.handler.PersonArrivalEventHandler;
import org.matsim.api.core.v01.events.handler.PersonDepartureEventHandler;
import org.matsim.api.core.v01.network.Link;
import org.matsim.api.core.v01.network.Network;
import org.matsim.api.core.v01.population.Person;
import org.matsim.core.controler.Controler;
import org.matsim.core.utils.geometry.CoordUtils;
import utils.Utils;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;

/**
 * Collects and processes data on the mode shares, based on the traveled
 * (euclidean) distance.
 * Can also produce statistics on the mode shares based on the number of
 * legs.
 *
 * @author thibautd adapted by anhorni
 */
public class ModeSharesEventHandler extends AbstractClassifiedFrequencyAnalysis
        implements PersonDepartureEventHandler, PersonArrivalEventHandler {
    private static final Logger log = Logger.getLogger(ModeSharesEventHandler.class);

    // use euclidean distance rather than network distance, as linkEvents
    // are not generated for non-car modes.
    private static final String ALL_MODES = "allAvailableModes";

    private final Map<Id, PersonDepartureEvent> pendantDepartures = new HashMap<Id, PersonDepartureEvent>();
    private final Network network;
    private boolean toReset = false;
    private double maxXYForPlotting = 100000; // [m]
    private String xy;

    public ModeSharesEventHandler(final Controler controler, String xy) {
        this.network = controler.getScenario().getNetwork();
        this.xy = xy;

        if (this.xy.equals("times")) {
            maxXYForPlotting = 3600.0;
        }
    }

    @Override
    public void reset(final int iteration) {
        // only reset at the first fired event, so that the order in which
        // reset and getResult methods are called at the end of the iteration
        // does not matter.
        this.toReset = true;
    }

    private void doReset() {
        if (this.toReset) {
            this.toReset = false;
            this.frequencies.clear();
            this.rawData.clear();

            if (this.pendantDepartures.size() > 0) {
                log.warn("Some arrivals were not handled!");
                this.pendantDepartures.clear();
            }
            this.maxXYForPlotting = 100000; // [m]
        }
    }

    @Override
    public void handleEvent(final PersonDepartureEvent event) {
        this.doReset();
        // catch the previous value to check consistency of the process
        PersonDepartureEvent old = this.pendantDepartures.put(event.getPersonId(), event);

        if (old != null) {
            log.warn("One departure were not handled before the following one " + " for agent "
                    + event.getPersonId());
        }
    }

    @Override
    public void handleEvent(final PersonArrivalEvent arrivalEvent) {
        this.doReset();
        PersonDepartureEvent departureEvent = this.pendantDepartures.remove(arrivalEvent.getPersonId());
        String mode = arrivalEvent.getLegMode();
        Frequency frequency;
        ResizableDoubleArray rawDataElement;

        // Consistency check...
        if (departureEvent == null) {
            log.warn("One arrival do not correspond to any departure for agent " + arrivalEvent.getPersonId());
            return;
        } else if (!mode.equals(departureEvent.getLegMode())) {
            log.warn("Departure and arrival have uncompatible modes!");
            return;
        }
        // consistency check... DONE

        if (this.frequencies.containsKey(mode)) {
            frequency = this.frequencies.get(mode);
            rawDataElement = this.rawData.get(mode);
        } else {
            frequency = new Frequency();
            rawDataElement = new ResizableDoubleArray();

            this.frequencies.put(mode, frequency);
            this.rawData.put(mode, rawDataElement);
        }
        double xyVal = 0.0;
        if (this.xy.equals("times")) {
            xyVal = this.computeTimes(arrivalEvent, departureEvent);
        } else {
            xyVal = this.computeDistances(arrivalEvent, departureEvent);
        }
        // remember data
        frequency.addValue(xyVal);
        rawDataElement.addElement(xyVal);
    }

    private double computeTimes(final PersonArrivalEvent arrivalEvent, final PersonDepartureEvent departureEvent) {
        return (arrivalEvent.getTime() - departureEvent.getTime());
    }

    private double computeDistances(final PersonArrivalEvent arrivalEvent,
            final PersonDepartureEvent departureEvent) {
        Link departureLink;
        Link arrivalLink;
        double distance;
        departureLink = this.network.getLinks().get(departureEvent.getLinkId());
        arrivalLink = this.network.getLinks().get(arrivalEvent.getLinkId());

        distance = CoordUtils.calcDistance(departureLink.getCoord(), arrivalLink.getCoord());

        return distance;
    }

    private Map<String, Double> getModeXYs() {
        Map<String, Double> modeXYs = new HashMap<String, Double>();
        double totalXY = 0d;
        double currentXY;

        for (String mode : this.rawData.keySet()) {
            currentXY = 0d;
            for (double d : this.rawData.get(mode).getElements()) {
                currentXY += d;
            }
            totalXY += currentXY;
            modeXYs.put(mode, currentXY);
        }
        modeXYs.put(ALL_MODES, totalXY);
        return modeXYs;
    }

    public void printInfo(final int iteration) {
        Map<String, Double> currentModeXYs = getModeXYs();
        double totalXY = currentModeXYs.remove(ALL_MODES);

        for (Map.Entry<String, Double> mode : currentModeXYs.entrySet()) {
            if (this.xy.equals("times")) {
                log.info("Share of " + mode.getKey() + ":\t" + "time [s]: " + (100d * mode.getValue() / totalXY)
                        + "%");
            } else {
                log.info("Share of " + mode.getKey() + ":\t" + "distance [km]: "
                        + (100d * mode.getValue() / totalXY) + "%");
            }
        }
    }

    /**
     * @return a {@link org.jfree.data.xy.XYSeries} representing the
     * distribution of traveled distances for the mode (histogramm),
     * or null if the mode is unknown (only modes that generated events
     * in the previous mobsim run are "known").
     *
     * @param step the width of the bins, in meters
     * @param mode the mode to get the data for
     */
    public XYSeries getTraveledXYsHistogram(final String mode, final double step) {
        boolean autoSort = false;
        boolean allowDuplicateXValues = true;
        XYSeries output = new XYSeries(mode, autoSort, allowDuplicateXValues);
        double[] modeRawData;
        try {
            modeRawData = this.rawData.get(mode).getElements();
        } catch (NullPointerException e) {
            return null;
        }
        Arrays.sort(modeRawData);
        double currentUpperBound = step;
        int count = 0;

        for (double xyValue : modeRawData) {
            if (xyValue < currentUpperBound) {
                count++;
            } else {
                // add the previous bin to the plot
                output.add(currentUpperBound - step, count);
                //output.add(currentUpperBound, count);

                currentUpperBound += step;
                while (xyValue > currentUpperBound) {
                    output.add(currentUpperBound, 0d);
                    currentUpperBound += step;
                }
                count = 1;
            }
        }
        //add the last count
        output.add(currentUpperBound - step, count);
        output.add(currentUpperBound, 0);
        return output;
    }

    public HistogramDataset getHistogramDataset(final int nBins) {
        HistogramDataset output = new HistogramDataset();
        output.setType(HistogramType.RELATIVE_FREQUENCY);
        ArrayList<Double> croppedData = new ArrayList<Double>();

        for (String mode : this.rawData.keySet()) {
            for (double d : this.rawData.get(mode).getElements()) {
                if (d < this.maxXYForPlotting) {
                    croppedData.add(d);
                }
            }
            output.addSeries(mode, Utils.convert(croppedData), nBins);
        }
        return output;
    }

    private JFreeChart getTraveledXYsHistogram(final int numberOfBins) {
        String title = this.xy + " distribution by mode";
        String xLabel = "distance (m)";
        if (this.xy.equals("times")) {
            xLabel = "time (s)";
        }
        String yLabel = "number of trips";
        boolean legend = true;
        boolean tooltips = false;
        boolean urls = false;
        HistogramDataset data = this.getHistogramDataset(numberOfBins);

        JFreeChart chart = ChartFactory.createHistogram(title, xLabel, yLabel, data, PlotOrientation.VERTICAL,
                legend, tooltips, urls);
        return chart;
    }

    public void writeXYsGraphic(final String fileName, final int numberOfBins) {
        JFreeChart chart = this.getTraveledXYsHistogram(numberOfBins);
        try {
            ChartUtilities.saveChartAsPNG(new File(fileName), chart, 1024, 768);
        } catch (IOException e) {
            log.error("got an error while trying to write graphics to file."
                    + " Error is not fatal, but output may be incomplete.");
            e.printStackTrace();
        }
    }

    @Override
    public void run(final Person person) {
        /*do nothing*/ }
}