View Javadoc
1   package nl.tudelft.simulation.dsol.swing.charts.xy;
2   
3   import java.util.ArrayList;
4   import java.util.Calendar;
5   import java.util.Iterator;
6   import java.util.List;
7   
8   import org.djutils.event.Event;
9   import org.djutils.event.EventListener;
10  import org.djutils.event.EventType;
11  import org.djutils.event.TimedEvent;
12  import org.djutils.logger.CategoryLogger;
13  import org.jfree.data.general.AbstractDataset;
14  
15  import nl.tudelft.simulation.dsol.simulators.SimulatorInterface;
16  import nl.tudelft.simulation.language.filters.FilterInterface;
17  import nl.tudelft.simulation.language.filters.ZeroFilter;
18  
19  /**
20   * The xySerie specifies an xySerie for XY Plots in DSOL.
21   * <p>
22   * Copyright (c) 2002-2023 Delft University of Technology, Jaffalaan 5, 2628 BX Delft, the Netherlands. All rights reserved. See
23   * for project information <a href="https://simulation.tudelft.nl/" target="_blank"> https://simulation.tudelft.nl</a>. The DSOL
24   * project is distributed under a three-clause BSD-style license, which can be found at
25   * <a href="https://https://simulation.tudelft.nl/dsol/docs/latest/license.html" target="_blank">
26   * https://https://simulation.tudelft.nl/dsol/docs/latest/license.html</a>.
27   * </p>
28   * @author <a href="https://www.linkedin.com/in/peterhmjacobs">Peter Jacobs </a>
29   * @since 1.5
30   */
31  public class XYSeries extends AbstractDataset implements EventListener
32  {
33      /** serial version UId. */
34      private static final long serialVersionUID = 1L;
35  
36      /** LOWER_RANGE_EVENT is fired on a range change. */
37      public static final EventType LOWER_RANGE_EVENT = new EventType("LOWER_RANGE_EVENT");
38  
39      /** UPPER_RANGE_EVENT is fired on a range change. */
40      public static final EventType UPPER_RANGE_EVENT = new EventType("UPPER_RANGE_EVENT");
41  
42      /** name refers to the name of the serie. */
43      private String name = null;
44  
45      /** the entries of the serie. */
46      protected List<double[]> entries = new ArrayList<double[]>();
47  
48      /** the simulator. */
49      private final SimulatorInterface<?> simulator;
50  
51      /** the axisType (default, logarithmic). */
52      private short axisType = XYChart.XLINEAR_YLINEAR;
53  
54      /** the filters of this dataset. */
55      private FilterInterface filter = new ZeroFilter();
56  
57      /** the period of this set. */
58      private final double PERIOD;
59  
60      /**
61       * constructs a new XYSeries.
62       * @param name String; the name of the series.
63       * @param simulator Simulator; the simulator
64       * @param axisType short; whether this serie is logarithmic (x=0 and y=0 are neglected)
65       * @param period double; the period of this series.
66       */
67      public XYSeries(final String name, final SimulatorInterface<?> simulator, final short axisType, final double period)
68      {
69          this.axisType = axisType;
70          this.name = name;
71          this.simulator = simulator;
72          this.PERIOD = period;
73          this.fireDatasetChanged();
74      }
75  
76      /** {@inheritDoc} */
77      @Override
78      public synchronized void notify(final Event event)
79      {
80          Number timeStamp;
81          if (event instanceof TimedEvent)
82          {
83              TimedEvent<?> timedEvent = (TimedEvent<?>) event;
84              if (timedEvent.getTimeStamp() instanceof Number)
85              {
86                  timeStamp = (Number) timedEvent.getTimeStamp();
87              }
88              else if (timedEvent.getTimeStamp() instanceof Calendar)
89              {
90                  timeStamp = ((Calendar) timedEvent.getTimeStamp()).getTimeInMillis();
91              }
92              else
93              {
94                  throw new IllegalArgumentException("TimedEvent has timestamp other than Number or Calendar");
95              }
96          }
97          else
98          {
99              timeStamp = (Number) this.simulator.getSimulatorTime();
100         }
101 
102         // We have chosen to simply neglect <=0.0 values on logarithmic axis
103         if (this.axisType == XYChart.XLOGARITHMIC_YLINEAR || this.axisType == XYChart.XLOGARITHMIC_YLOGARITHMIC)
104         {
105             if (timeStamp.doubleValue() <= 0.0)
106             {
107                 CategoryLogger.always().warn("notify: refusing xvalue of {} on logrithmic chart", event);
108                 return;
109             }
110         }
111         if (this.axisType == XYChart.XLINEAR_YLOGARITHMIC || this.axisType == XYChart.XLOGARITHMIC_YLOGARITHMIC)
112         {
113             if (((Number) event.getContent()).doubleValue() <= 0.0)
114             {
115                 CategoryLogger.always().warn("notify: refusing yValue of {} on logrithmic chart", event);
116                 return;
117             }
118         }
119         double[] point = {timeStamp.doubleValue(), ((Number) event.getContent()).doubleValue()};
120         if (!this.filter.accept(point))
121         {
122             return;
123         }
124         this.entries.add(point);
125         if (!(Double.isInfinite(this.PERIOD)))
126         {
127             double lowerBounds = point[0] - this.PERIOD;
128             for (Iterator<double[]> i = this.entries.iterator(); i.hasNext();)
129             {
130                 double[] entry = i.next();
131                 if (entry[0] < lowerBounds)
132                 {
133                     i.remove();
134                 }
135                 else
136                 {
137                     break;
138                 }
139             }
140         }
141         this.fireDatasetChanged();
142     }
143 
144     /**
145      * returns the number of items in this series.
146      * @return int the number
147      */
148     public int getItemCount()
149     {
150         return this.entries.size();
151     }
152 
153     /**
154      * returns the X value.
155      * @param item int; the item
156      * @return Number the xValue
157      */
158     public synchronized double getXValue(int item)
159     {
160         item = Math.min(item, this.entries.size() - 1);
161         return this.entries.get(item)[0];
162     }
163 
164     /**
165      * returns the yValue.
166      * @param item int; the item
167      * @return Number
168      */
169     public double getYValue(int item)
170     {
171         item = Math.min(item, this.entries.size() - 1);
172         return this.entries.get(item)[1];
173     }
174 
175     /**
176      * returns the name of this series.
177      * @return String name
178      */
179     public String getSeriesName()
180     {
181         return this.name;
182     }
183 
184     /**
185      * applies a filter on the chart.
186      * @param filter FilterInterface; the filter to apply
187      */
188     public void setFilter(final FilterInterface filter)
189     {
190         this.filter = filter;
191     }
192 }