AbstractContourPlot.java

  1. package org.opentrafficsim.draw.graphs;

  2. import java.awt.Color;
  3. import java.awt.event.ActionEvent;
  4. import java.awt.event.ActionListener;

  5. import org.djunits.value.vdouble.scalar.Time;
  6. import org.djutils.exceptions.Throw;
  7. import org.jfree.chart.JFreeChart;
  8. import org.jfree.chart.LegendItem;
  9. import org.jfree.chart.LegendItemCollection;
  10. import org.jfree.chart.axis.NumberAxis;
  11. import org.jfree.chart.plot.XYPlot;
  12. import org.jfree.data.DomainOrder;
  13. import org.opentrafficsim.core.dsol.OTSSimulatorInterface;
  14. import org.opentrafficsim.draw.core.BoundsPaintScale;
  15. import org.opentrafficsim.draw.graphs.ContourDataSource.ContourDataType;
  16. import org.opentrafficsim.draw.graphs.ContourDataSource.Dimension;

  17. /**
  18.  * Class for contour plots. The data that is plotted is stored in a {@code ContourDataSource}, which may be shared among several
  19.  * contour plots along the same path. This abstract class takes care of the interactions between the plot and the data pool. Sub
  20.  * classes only need to specify a few plot specific variables and functionalities.
  21.  * <p>
  22.  * Copyright (c) 2013-2020 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved. <br>
  23.  * BSD-style license. See <a href="http://opentrafficsim.org/node/13">OpenTrafficSim License</a>.
  24.  * <p>
  25.  * @version $Revision$, $LastChangedDate$, by $Author$, initial version 4 okt. 2018 <br>
  26.  * @author <a href="http://www.tbm.tudelft.nl/averbraeck">Alexander Verbraeck</a>
  27.  * @author <a href="http://www.tudelft.nl/pknoppers">Peter Knoppers</a>
  28.  * @author <a href="http://www.transport.citg.tudelft.nl">Wouter Schakel</a>
  29.  * @param <Z> z-value type
  30.  */
  31. public abstract class AbstractContourPlot<Z extends Number> extends AbstractSamplerPlot
  32.         implements XYInterpolatedDataset, ActionListener
  33. {

  34.     /** Color scale for the graph. */
  35.     private final BoundsPaintScale paintScale;

  36.     /** Difference of successive values in the legend. */
  37.     private final Z legendStep;

  38.     /** Format string used to create the captions in the legend. */
  39.     private final String legendFormat;

  40.     /** Format string used to create status label (under the mouse). */
  41.     private final String valueFormat;

  42.     /** Data pool. */
  43.     private final ContourDataSource<?> dataPool;

  44.     /** Block renderer in chart. */
  45.     private XYInterpolatedBlockRenderer blockRenderer = null;

  46.     /**
  47.      * Constructor with specified paint scale.
  48.      * @param caption String; caption
  49.      * @param simulator OTSSimulatorInterface; simulator
  50.      * @param dataPool ContourDataSource&lt;?&gt;; data pool
  51.      * @param paintScale BoundsPaintScale; paint scale
  52.      * @param legendStep Z; increment between color legend entries
  53.      * @param legendFormat String; format string for the captions in the color legend
  54.      * @param valueFormat String; format string used to create status label (under the mouse)
  55.      */
  56.     public AbstractContourPlot(final String caption, final OTSSimulatorInterface simulator, final ContourDataSource<?> dataPool,
  57.             final BoundsPaintScale paintScale, final Z legendStep, final String legendFormat, final String valueFormat)
  58.     {
  59.         super(caption, dataPool.getUpdateInterval(), simulator, dataPool.getSampler(), dataPool.getPath(), dataPool.getDelay());
  60.         dataPool.registerContourPlot(this);
  61.         this.dataPool = dataPool;
  62.         this.paintScale = paintScale;
  63.         this.legendStep = legendStep;
  64.         this.legendFormat = legendFormat;
  65.         this.valueFormat = valueFormat;
  66.         this.blockRenderer = new XYInterpolatedBlockRenderer(this);
  67.         this.blockRenderer.setPaintScale(this.paintScale);
  68.         this.blockRenderer.setBlockHeight(dataPool.getGranularity(Dimension.DISTANCE));
  69.         this.blockRenderer.setBlockWidth(dataPool.getGranularity(Dimension.TIME));
  70.         setChart(createChart());
  71.     }

  72.     /**
  73.      * Constructor with default paint scale.
  74.      * @param caption String; caption
  75.      * @param simulator OTSSimulatorInterface; simulator
  76.      * @param dataPool ContourDataSource&lt;?&gt;; data pool
  77.      * @param legendStep Z; increment between color legend entries
  78.      * @param legendFormat String; format string for the captions in the color legend
  79.      * @param minValue Z; minimum value
  80.      * @param maxValue Z; maximum value
  81.      * @param valueFormat String; format string used to create status label (under the mouse)
  82.      */
  83.     @SuppressWarnings("parameternumber")
  84.     public AbstractContourPlot(final String caption, final OTSSimulatorInterface simulator, final ContourDataSource<?> dataPool,
  85.             final Z legendStep, final String legendFormat, final Z minValue, final Z maxValue, final String valueFormat)
  86.     {
  87.         this(caption, simulator, dataPool, createPaintScale(minValue, maxValue), legendStep, legendFormat, valueFormat);
  88.     }

  89.     /**
  90.      * Creates a default paint scale from red, via yellow to green.
  91.      * @param minValue Number; minimum value
  92.      * @param maxValue Number; maximum value
  93.      * @return BoundsPaintScale; default paint scale
  94.      */
  95.     private static BoundsPaintScale createPaintScale(final Number minValue, final Number maxValue)
  96.     {
  97.         Throw.when(minValue.doubleValue() >= maxValue.doubleValue(), IllegalArgumentException.class,
  98.                 "Minimum value %s is below or equal to maxumum value %s.", minValue, maxValue);
  99.         double[] boundaries =
  100.                 {minValue.doubleValue(), (minValue.doubleValue() + maxValue.doubleValue()) / 2.0, maxValue.doubleValue()};
  101.         Color[] colorValues = {Color.RED, Color.YELLOW, Color.GREEN};
  102.         return new BoundsPaintScale(boundaries, colorValues);
  103.     }

  104.     /**
  105.      * Create a chart.
  106.      * @return JFreeChart; chart
  107.      */
  108.     private JFreeChart createChart()
  109.     {
  110.         NumberAxis xAxis = new NumberAxis("Time [s] \u2192");
  111.         NumberAxis yAxis = new NumberAxis("Distance [m] \u2192");
  112.         XYPlot plot = new XYPlot(this, xAxis, yAxis, this.blockRenderer);
  113.         LegendItemCollection legend = new LegendItemCollection();
  114.         for (int i = 0;; i++)
  115.         {
  116.             double value = this.paintScale.getLowerBound() + i * this.legendStep.doubleValue();
  117.             if (value > this.paintScale.getUpperBound() + 1e-6)
  118.             {
  119.                 break;
  120.             }
  121.             legend.add(new LegendItem(String.format(this.legendFormat, scale(value)), this.paintScale.getPaint(value)));
  122.         }
  123.         legend.add(new LegendItem("No data", Color.BLACK));
  124.         plot.setFixedLegendItems(legend);
  125.         final JFreeChart chart = new JFreeChart(getCaption(), plot);
  126.         return chart;
  127.     }

  128.     /**
  129.      * Returns the time granularity, just for information.
  130.      * @return double; time granularity
  131.      */
  132.     public double getTimeGranularity()
  133.     {
  134.         return this.dataPool.getGranularity(Dimension.TIME);
  135.     }

  136.     /**
  137.      * Returns the space granularity, just for information.
  138.      * @return double; space granularity
  139.      */
  140.     public double getSpaceGranularity()
  141.     {
  142.         return this.dataPool.getGranularity(Dimension.DISTANCE);
  143.     }

  144.     /**
  145.      * Sets the correct space granularity radio button to selected. This is done from a {@code DataPool} to keep multiple plots
  146.      * consistent.
  147.      * @param granularity double; space granularity
  148.      */
  149.     public final void setSpaceGranularity(final double granularity)
  150.     {
  151.         this.blockRenderer.setBlockHeight(granularity);
  152.     }

  153.     /**
  154.      * Sets the correct time granularity radio button to selected. This is done from a {@code DataPool} to keep multiple plots
  155.      * consistent.
  156.      * @param granularity double; time granularity
  157.      */
  158.     public final void setTimeGranularity(final double granularity)
  159.     {
  160.         this.blockRenderer.setBlockWidth(granularity);
  161.     }

  162.     /**
  163.      * Sets the check box for interpolated rendering and block renderer setting. This is done from a {@code DataPool} to keep
  164.      * multiple plots consistent.
  165.      * @param interpolate boolean; selected or not
  166.      */
  167.     public final void setInterpolation(final boolean interpolate)
  168.     {
  169.         this.blockRenderer.setInterpolate(interpolate);
  170.     }
  171.    
  172.     /**
  173.      * Returns the data pool for sub classes.
  174.      * @return ContourDataSource; data pool for subclasses
  175.      */
  176.     public final ContourDataSource<?> getDataPool()
  177.     {
  178.         return this.dataPool;
  179.     }

  180.     /** {@inheritDoc} */
  181.     @Override
  182.     public final int getItemCount(final int series)
  183.     {
  184.         return this.dataPool.getBinCount(Dimension.DISTANCE) * this.dataPool.getBinCount(Dimension.TIME);
  185.     }

  186.     /** {@inheritDoc} */
  187.     @Override
  188.     public final Number getX(final int series, final int item)
  189.     {
  190.         return getXValue(series, item);
  191.     }

  192.     /** {@inheritDoc} */
  193.     @Override
  194.     public final double getXValue(final int series, final int item)
  195.     {
  196.         return this.dataPool.getAxisValue(Dimension.TIME, item);
  197.     }

  198.     /** {@inheritDoc} */
  199.     @Override
  200.     public final Number getY(final int series, final int item)
  201.     {
  202.         return getYValue(series, item);
  203.     }

  204.     /** {@inheritDoc} */
  205.     @Override
  206.     public final double getYValue(final int series, final int item)
  207.     {
  208.         return this.dataPool.getAxisValue(Dimension.DISTANCE, item);
  209.     }

  210.     /** {@inheritDoc} */
  211.     @Override
  212.     public final Number getZ(final int series, final int item)
  213.     {
  214.         return getZValue(series, item);
  215.     }

  216.     /** {@inheritDoc} */
  217.     @Override
  218.     public final Comparable<String> getSeriesKey(final int series)
  219.     {
  220.         return getCaption();
  221.     }

  222.     /** {@inheritDoc} */
  223.     @SuppressWarnings("rawtypes")
  224.     @Override
  225.     public final int indexOf(final Comparable seriesKey)
  226.     {
  227.         return 0;
  228.     }

  229.     /** {@inheritDoc} */
  230.     @Override
  231.     public final DomainOrder getDomainOrder()
  232.     {
  233.         return DomainOrder.ASCENDING;
  234.     }

  235.     /** {@inheritDoc} */
  236.     @Override
  237.     public final double getZValue(final int series, final int item)
  238.     {
  239.         // default 1 series
  240.         return getValue(item, this.dataPool.getGranularity(Dimension.DISTANCE), this.dataPool.getGranularity(Dimension.TIME));
  241.     }

  242.     /** {@inheritDoc} */
  243.     @Override
  244.     public final int getSeriesCount()
  245.     {
  246.         return 1; // default
  247.     }

  248.     /** {@inheritDoc} */
  249.     @Override
  250.     public int getRangeBinCount()
  251.     {
  252.         return this.dataPool.getBinCount(Dimension.DISTANCE);
  253.     }

  254.     /**
  255.      * Returns the status label when the mouse is over the given location.
  256.      * @param domainValue double; domain value (x-axis)
  257.      * @param rangeValue double; range value (y-axis)
  258.      * @return String; status label when the mouse is over the given location
  259.      */
  260.     @Override
  261.     public final String getStatusLabel(final double domainValue, final double rangeValue)
  262.     {
  263.         if (this.dataPool == null)
  264.         {
  265.             return String.format("time %.0fs, distance %.0fm", domainValue, rangeValue);
  266.         }
  267.         int i = this.dataPool.getAxisBin(Dimension.DISTANCE, rangeValue);
  268.         int j = this.dataPool.getAxisBin(Dimension.TIME, domainValue);
  269.         int item = j * this.dataPool.getBinCount(Dimension.DISTANCE) + i;
  270.         double zValue = scale(
  271.                 getValue(item, this.dataPool.getGranularity(Dimension.DISTANCE), this.dataPool.getGranularity(Dimension.TIME)));
  272.         return String.format("time %.0fs, distance %.0fm, " + this.valueFormat, domainValue, rangeValue, zValue);
  273.     }

  274.     /** {@inheritDoc} */
  275.     @Override
  276.     protected final void increaseTime(final Time time)
  277.     {
  278.         if (this.dataPool != null) // dataPool is null at construction
  279.         {
  280.             this.dataPool.increaseTime(time);
  281.         }
  282.     }

  283.     /**
  284.      * Obtain value for cell from the data pool.
  285.      * @param item int; item number
  286.      * @param cellLength double; cell length
  287.      * @param cellSpan double; cell duration
  288.      * @return double; value for cell from the data pool
  289.      */
  290.     protected abstract double getValue(int item, double cellLength, double cellSpan);

  291.     /**
  292.      * Scale the value from SI to the desired unit for users.
  293.      * @param si double; SI value
  294.      * @return double; scaled value
  295.      */
  296.     protected abstract double scale(double si);

  297.     /**
  298.      * Returns the contour data type for use in a {@code ContourDataSource}.
  299.      * @return CountorDataType; contour data type
  300.      */
  301.     protected abstract ContourDataType<Z, ?> getContourDataType();

  302.     public XYInterpolatedBlockRenderer getBlockRenderer()
  303.     {
  304.         return blockRenderer;
  305.     }

  306.     @Override
  307.     public final void actionPerformed(final ActionEvent actionEvent)
  308.     {
  309.         String command = actionEvent.getActionCommand();
  310.         if (command.equalsIgnoreCase("setSpaceGranularity"))
  311.         {
  312.             // The source field is abused to contain the granularity
  313.             double granularity = (double) actionEvent.getSource();
  314.             setSpaceGranularity(granularity);
  315.         }
  316.         else if (command.equalsIgnoreCase("setTimeGranularity"))
  317.         {
  318.             // The source field is abused to contain the granularity
  319.             double granularity = (double) actionEvent.getSource();
  320.             setTimeGranularity(granularity);
  321.         }
  322.         else
  323.         {
  324.             throw new RuntimeException("Unhandled ActionEvent");
  325.         }
  326.     }

  327. }